Re: Safe manipulation of RDF data (from semantic-web)

Hi Mikael,

the template URI on its own is irrelevant here, it could be a blank
node resource. It becomes important when one intends to reuse
templates, e.g. extend them or reference them, possibly from another
LDT ontology.

Yes it is the ldt:match that holds the URI template that request URI
is matched against. I have expanded the explanation here:
https://github.com/AtomGraph/Processor/wiki/Linked-Data-Templates#templates

As for the agent ID, one option to pass a value to the LDT template is
using template parameters:
https://github.com/AtomGraph/Processor/wiki/Linked-Data-Templates#parameters

Then if a request URI is
https://resource.lingsoft.fi/286c384d-cd5c-4887-9b85-94c0c147f709?agent=123456,
a variable binding (?agent, "123456") is applied to the query string
from ldt:query, before it is executed.
This might or might not work for your use case.

Martynas

On Tue, Sep 17, 2019 at 1:43 PM Mikael Pesonen
<mikael.pesonen@lingsoft.fi> wrote:
>
>
> Hmm, still mixing up things. So ldt:match has to match the resource URI.
>
> On 17/09/2019 12:13, Mikael Pesonen wrote:
> >
> > Ok now I got it, the template address has to be the same as resource URI.
> > Just one question, in our case, https://base/{uuid}, how should we
> > forward the agent id (access level) to the template for utilizing ACL?
> >
> >
> >
> > On 17/09/2019 11:40, Martynas Jusevičius wrote:
> >> Thanks Mikael,
> >>
> >> the example makes it clearer.
> >>
> >> So the URI template for all persons (and I guess all resources in
> >> general?) is "/{uuid}", if we take https://resource.lingsoft.fi as the
> >> base URI. Which means that you could not match two different LDT
> >> templates for different types of persons.
> >>
> >> Then my suggestion with using a single template with a query that
> >> references the ACL graph still stands. Let me know if you need help
> >> setting it up in Processor.
> >>
> >> Martynas
> >>
> >> On Mon, Sep 16, 2019 at 11:27 AM Mikael Pesonen
> >> <mikael.pesonen@lingsoft.fi> wrote:
> >>>
> >>> Here is a sample data:
> >>>
> >>> <https://resource.lingsoft.fi/286c384d-cd5c-4887-9b85-94c0c147f709>
> >>>           a                        foaf:Person ;
> >>>           vcard:family-name        "Pesonen" ;
> >>>           vcard:fn                 "Mikael Pesonen" ;
> >>>           vcard:given-name         "Mikael" ;
> >>>           vcard:hasEmail
> >>> <https://resource.lingsoft.fi/cf9b02b7-bd0d-486e-b0d9-da1464e27d2e> ,
> >>> <https://resource.lingsoft.fi/5c04aa23-6c42-44a1-9ac9-69ee255ac170> ;
> >>>           vcard:hasGender          vcard:Male ;
> >>>           vcard:hasInstantMessage
> >>> <https://resource.lingsoft.fi/4aa01d37-744c-4964-a794-d997aa376584> ;
> >>>           vcard:hasPhoto
> >>> <https://resource.lingsoft.fi/8f4a4ddd-43c2-4e27-8ed7-996dd00e939c> ;
> >>>           vcard:hasTelephone
> >>> <https://resource.lingsoft.fi/3755ed0c-81b7-430e-92a0-16fc80ba41b4> ;
> >>>           org:basedAt
> >>> <https://resource.lingsoft.fi/b48a0820-6921-43fc-a346-e72397265bbe> ;
> >>>           org:memberOf
> >>> <https://resource.lingsoft.fi/810dfbff-e6fb-458a-b27d-3726a27e5109> ;
> >>>           foaf:account
> >>> <https://resource.lingsoft.fi/2f0aa772-f845-4f43-b607-dc65ff66b9aa> ;
> >>> <https://resource.lingsoft.fi/cf9b02b7-bd0d-486e-b0d9-da1464e27d2e>
> >>>           a                         vcard:Email , vcard:Work ;
> >>>           rdfs:label                "***@lingsoft.fi" ;
> >>>           vcard:hasValue <mailto:***@lingsoft.fi> .
> >>>
> >>>
> >>> So most of the person's values are resources and every resource has id
> >>> of type https://resource.lingsoft.fi/<UUID>.
> >>>
> >>>
> >>> Mikael
> >>>
> >>>
> >>> On 15/09/2019 01:02, Martynas Jusevičius wrote:
> >>>> I meant the first and the ACL examples as alternatives, but yes you
> >>>> can combine the approaches as well. Again, depends mostly on your URIs
> >>>> - and are able to change their pattern?
> >>>>
> >>>> I think it would help if you could show some RDF data that represents
> >>>> your case (does not have to be the actual person data :)) Either paste
> >>>> inline or as a Gist if it's larger.
> >>>>
> >>>> Re. ACL, we use a filter in our LinkedDataHub platform that checks ACL
> >>>> access before the actual LDT request is invoked. And if query results
> >>>> need to depend on the access level, we reference the ACL dataset as I
> >>>> showed in the example.
> >>>>
> >>>> Martynas
> >>>>
> >>>> On Fri, Sep 13, 2019 at 3:55 PM Mikael Pesonen
> >>>> <mikael.pesonen@lingsoft.fi> wrote:
> >>>>> Looking at your first example, looks like that and this acl
> >>>>> version work
> >>>>> both?
> >>>>>
> >>>>> So as with your first example:
> >>>>>
> >>>>> /person/basic_access/{id}
> >>>>> --
> >>>>>
> >>>>> :BasicPersonAccessItem a ldt:Template ;
> >>>>>        ldt:match "/person/basic_access/{id}" ;
> >>>>>        ldt:query :ConstructBasicPerson ;
> >>>>>
> >>>>> ----
> >>>>> /person/admin_access/{id}
> >>>>> --
> >>>>> :AdminPersonAccessItem a ldt:Template ;
> >>>>>        ldt:match "/person/admin_access/{id}" ;
> >>>>>        ldt:query :ConstructFullPerson ;
> >>>>>
> >>>>>
> >>>>>
> >>>>> And this acl example
> >>>>>
> >>>>> /person/{agent}/{id}
> >>>>> --
> >>>>> :PersonAccessItem a ldt:Template ;
> >>>>>        ldt:match "/person/{agent}/{id}" ;
> >>>>>        ldt:query :ConstructPerson ;
> >>>>> ...
> >>>>>
> >>>>>
> >>>>> ACL example sure is more refined since you can define the access
> >>>>> levels
> >>>>> in the ACL data.
> >>>>>
> >>>>>
> >>>>> On 13/09/2019 16:25, Martynas Jusevičius wrote:
> >>>>>> Well if you only have one kind of person resources with a single URI
> >>>>>> pattern, then you cannot select (match) different LDT templates.
> >>>>>> That is because an LDT template maps one URI pattern to one SPARQL
> >>>>>> command. The matching process is not looking into the SPARQL query
> >>>>>> results at all, only at the request URI and the application's LDT
> >>>>>> ontology.
> >>>>>>
> >>>>>> I think you can solve this with a single query though. What we do is
> >>>>>> provide the URI of the requesting agent as a query binding, e.g.
> >>>>>> ?agent variable. Something like
> >>>>>>
> >>>>>> :ConstructPerson a sp:Construct ;
> >>>>>>        sp:text """
> >>>>>> PREFIX  foaf: <http://xmlns.com/foaf/0.1/>
> >>>>>> PREFIX  acl:  <http://www.w3.org/ns/auth/acl#>
> >>>>>>
> >>>>>> CONSTRUCT
> >>>>>>      {
> >>>>>>        ?this a foaf:Person .
> >>>>>>        ?this foaf:name ?name .
> >>>>>>        ?this ?p ?o .
> >>>>>>      }
> >>>>>> WHERE
> >>>>>>      {   { ?this  a                     foaf:Person ;
> >>>>>>                   foaf:name             ?name
> >>>>>>          }
> >>>>>>        UNION
> >>>>>>          { GRAPH <acl>
> >>>>>>              { ?auth  acl:accessTo  ?this ;
> >>>>>>                     acl:agent ?agent .
> >>>>>>              }
> >>>>>>            ?this  ?p  ?o
> >>>>>>          }
> >>>>>>      }
> >>>>>>        """ ;
> >>>>>>        rdfs:isDefinedBy : .
> >>>>>>
> >>>>>> The idea is that the person query always returns "basic" properties,
> >>>>>> and adds all properties *only* if the agent ?agent has an
> >>>>>> authorization to access the requested resource ?this.
> >>>>>> This approach requires that the query has access to the ACL data,
> >>>>>> which I have indicated here as GRAPH <acl>. The actual pattern for
> >>>>>> authorization check will probably be more complex of course.
> >>>>>> It also requires that the authentication mechanism can provide
> >>>>>> the URI
> >>>>>> of the agent.
> >>>>>>
> >>>>>> I hope I got what you meant :)
> >>>>>>
> >>>>>> On Fri, Sep 13, 2019 at 2:58 PM Mikael Pesonen
> >>>>>> <mikael.pesonen@lingsoft.fi> wrote:
> >>>>>>> Ah, I might have explained our case bit vaguely. So I just meant
> >>>>>>> that we
> >>>>>>> have in RDF data one kind of person resources, and
> >>>>>>> depending on the access rights in the application, you are
> >>>>>>> allowed to
> >>>>>>> see different portions of that person's data.
> >>>>>>> Basic user sees only the name, for example, and admin user is
> >>>>>>> allowed to
> >>>>>>> see all data. This is handled by selecting different template
> >>>>>>> for basic
> >>>>>>> user and admin, right?
> >>>>>>>
> >>>>>>>
> >>>>>>>
> >>>>>>> On 13/09/2019 15:52, Martynas Jusevičius wrote:
> >>>>>>>> Mikael,
> >>>>>>>>
> >>>>>>>> this is related to hierarchical URIs:
> >>>>>>>> http://patterns.dataincubator.org/book/hierarchical-uris.html
> >>>>>>>>
> >>>>>>>> In your case, the question is how you have organized the
> >>>>>>>> collections/items of basic and admin persons in your dataset.
> >>>>>>>>
> >>>>>>>> One option is that both "basic persons" and "admin persons"
> >>>>>>>> belong to
> >>>>>>>> the same collection and have a single URI pattern: /persons/{id}
> >>>>>>>> In this case you cannot tell if resource /persons/12345 is a
> >>>>>>>> "basic
> >>>>>>>> person" or "admin person" just from its URI. You need to
> >>>>>>>> dereference
> >>>>>>>> it and the look into RDF types and properties.
> >>>>>>>>
> >>>>>>>> Another option is that you treat them as belonging to separate
> >>>>>>>> collections, for example: /persons/{id} and /admins/{id}
> >>>>>>>> In this case you can easily tell if a resource is a "basic
> >>>>>>>> person" or
> >>>>>>>> an "admin person" already from its URIs.
> >>>>>>>>
> >>>>>>>> Linked Data Templates are best suited for this second case,
> >>>>>>>> where URI
> >>>>>>>> space is subdivided into hierarchies based on entity types.
> >>>>>>>> That makes
> >>>>>>>> it easy to define URI templates that match precisely the set of
> >>>>>>>> resources that you want.
> >>>>>>>>
> >>>>>>>> Does it make it clearer?
> >>>>>>>>
> >>>>>>>> On Fri, Sep 13, 2019 at 2:08 PM Mikael Pesonen
> >>>>>>>> <mikael.pesonen@lingsoft.fi> wrote:
> >>>>>>>>> Hi Martynas,
> >>>>>>>>>
> >>>>>>>>> thank you for the examples, GET seems clear now.
> >>>>>>>>>
> >>>>>>>>> Good point about the person / document. We probably end up
> >>>>>>>>> with three
> >>>>>>>>> kind of resources: actual object, admin record (who last
> >>>>>>>>> modified etc),
> >>>>>>>>> and web page or another document about the object.
> >>>>>>>>>
> >>>>>>>>> Just one question: what did you mean by
> >>>>>>>>>
> >>>>>>>>> "If you cannot distinguish "basic person" from "admin person"
> >>>>>>>>> by their
> >>>>>>>>> URIs"?
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> We are not quite there yet with updates, so we might have
> >>>>>>>>> questions
> >>>>>>>>> later about those.
> >>>>>>>>>
> >>>>>>>>> Br,
> >>>>>>>>> Mikael
> >>>>>>>>>
> >>>>>>>>> On 11/09/2019 18:45, Martynas Jusevičius wrote:
> >>>>>>>>>> Hi Mikael,
> >>>>>>>>>>
> >>>>>>>>>> thanks for reaching out.
> >>>>>>>>>>
> >>>>>>>>>> There is more information on LDT in the AtomGraph Processor
> >>>>>>>>>> wiki, more
> >>>>>>>>>> specifically:
> >>>>>>>>>> https://github.com/AtomGraph/Processor/wiki/Linked-Data-Templates
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>> The matching is based on URIs: relative request URI is being
> >>>>>>>>>> matched
> >>>>>>>>>> against the ldt:match values of templates in the ontology.
> >>>>>>>>>>
> >>>>>>>>>> Then, from the matching template (if there is any), the
> >>>>>>>>>> SPARQL command
> >>>>>>>>>> is retrieved using either ldt:query or ldt:update (depending
> >>>>>>>>>> on the
> >>>>>>>>>> HTTP request method).
> >>>>>>>>>>
> >>>>>>>>>> To address your example, templates and queries could look
> >>>>>>>>>> like this:
> >>>>>>>>>>
> >>>>>>>>>> :BasicPersonItem a ldt:Template ;
> >>>>>>>>>>          ldt:match "/person/basic/{id}" ;
> >>>>>>>>>>          ldt:query :ConstructBasicPerson ;
> >>>>>>>>>>          rdfs:isDefinedBy : .
> >>>>>>>>>>
> >>>>>>>>>> :ConstructBasicPerson a sp:Construct ;
> >>>>>>>>>>          sp:text """
> >>>>>>>>>>          PREFIX  foaf: <http://xmlns.com/foaf/0.1/>
> >>>>>>>>>>
> >>>>>>>>>>          CONSTRUCT
> >>>>>>>>>>          {
> >>>>>>>>>>              ?this a foaf:Person ;
> >>>>>>>>>>                  foaf:name ?name .
> >>>>>>>>>>          }
> >>>>>>>>>>          {
> >>>>>>>>>>              ?this a foaf:Person ;
> >>>>>>>>>>                  foaf:name ?name .
> >>>>>>>>>>          }
> >>>>>>>>>>          """ ;
> >>>>>>>>>>          rdfs:isDefinedBy : .
> >>>>>>>>>>
> >>>>>>>>>> :AdminPersonItem a ldt:Template ;
> >>>>>>>>>>          ldt:match "/person/admin/{id}" ;
> >>>>>>>>>>          ldt:query :ConstructAdminPerson ;
> >>>>>>>>>>          rdfs:isDefinedBy : .
> >>>>>>>>>>
> >>>>>>>>>> :ConstructAdminPerson a sp:Construct ;
> >>>>>>>>>>          sp:text """
> >>>>>>>>>>          CONSTRUCT WHERE
> >>>>>>>>>>          {
> >>>>>>>>>>              ?this ?p ?o
> >>>>>>>>>>          }
> >>>>>>>>>>          """ ;
> >>>>>>>>>>          rdfs:isDefinedBy : .
> >>>>>>>>>>
> >>>>>>>>>> "Basic person" query retrieves only name and type, "admin
> >>>>>>>>>> person"
> >>>>>>>>>> query retrieves all properties.
> >>>>>>>>>> This example requires that basic and admin person resources
> >>>>>>>>>> can be
> >>>>>>>>>> differentiated by their URIs, i.e. "/person/basic/{id}" vs
> >>>>>>>>>> "/person/admin/{id}".
> >>>>>>>>>>
> >>>>>>>>>> It also assumes that persons are documents (since they can be
> >>>>>>>>>> dereferenced over HTTP), which is not kosher re. httpRange-14
> >>>>>>>>>> [1]. A
> >>>>>>>>>> better solution would have separate resources for persons
> >>>>>>>>>> e.g. using
> >>>>>>>>>> hash URIs such as #this) and explicitly connect them to
> >>>>>>>>>> documents
> >>>>>>>>>> using an RDF property. We use
> >>>>>>>>>> foaf:primaryTopic/foaf:isPrimaryTopicOf.
> >>>>>>>>>> But this is a whole topic on its own.
> >>>>>>>>>>
> >>>>>>>>>> If you cannot distinguish "basic person" from "admin person"
> >>>>>>>>>> by their
> >>>>>>>>>> URIs, you could also have a template that matches both and
> >>>>>>>>>> maps to a
> >>>>>>>>>> single query. The question is then whether you can
> >>>>>>>>>> differentiate which
> >>>>>>>>>> properties to return using a single query.
> >>>>>>>>>>
> >>>>>>>>>> Does this help?
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>> [1] https://www.w3.org/2001/tag/group/track/issues/14
> >>>>>>>>>>
> >>>>>>>>>> Martynas
> >>>>>>>>>> atomgraph.com
> >>>>>>>>>>
> >>>>>>>>>> On Wed, Sep 11, 2019 at 11:21 AM Mikael Pesonen
> >>>>>>>>>> <mikael.pesonen@lingsoft.fi> wrote:
> >>>>>>>>>>> Hi Martynas,
> >>>>>>>>>>>
> >>>>>>>>>>> we have a proprietary implementation now:
> >>>>>>>>>>>
> >>>>>>>>>>> js/React app generates a custom json out of form data. That
> >>>>>>>>>>> is sent
> >>>>>>>>>>> (with a template id) to also custom proxy, which converts
> >>>>>>>>>>> the json into
> >>>>>>>>>>> SPARQL using pre made templates. SPARQL is then queried on
> >>>>>>>>>>> Apache Jena.
> >>>>>>>>>>>
> >>>>>>>>>>> Now we would like to replace all custom bits with one ore
> >>>>>>>>>>> more standards.
> >>>>>>>>>>>
> >>>>>>>>>>> Is it possible to have any kind of templates with LDT? For
> >>>>>>>>>>> example
> >>>>>>>>>>> "person_basic" and "person_admin",
> >>>>>>>>>>> where admin contains more properties of a person?
> >>>>>>>>>>>
> >>>>>>>>>>> I'm still having trouble to understand how the SPARQL
> >>>>>>>>>>> template is
> >>>>>>>>>>> selected with LDT.
> >>>>>>>>>>>
> >>>>>>>>>>> Br,
> >>>>>>>>>>> Mikael
> >>>>>>>>>>>
> >>>>>>>>>>> On 10/09/2019 15:50, Martynas Jusevičius wrote:
> >>>>>>>>>>>> Hey Mikael,
> >>>>>>>>>>>>
> >>>>>>>>>>>> we have a simple example here:
> >>>>>>>>>>>> https://github.com/AtomGraph/Processor#example
> >>>>>>>>>>>>
> >>>>>>>>>>>> Do you have some specific use case in mind? If you can
> >>>>>>>>>>>> share it, I can
> >>>>>>>>>>>> probably look into it.
> >>>>>>>>>>>>
> >>>>>>>>>>>> There is a Community Group for Linked Data Templates which
> >>>>>>>>>>>> includes a
> >>>>>>>>>>>> mailing list: https://www.w3.org/community/declarative-apps/
> >>>>>>>>>>>>
> >>>>>>>>>>>> Martynas
> >>>>>>>>>>>> atomgraph.com
> >>>>>>>>>>>>
> >>>>>>>>>>>> On Tue, Sep 10, 2019 at 1:27 PM Mikael Pesonen
> >>>>>>>>>>>> <mikael.pesonen@lingsoft.fi> wrote:
> >>>>>>>>>>>>> In the example there is the GET request
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> GET
> >>>>>>>>>>>>> /people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762
> >>>>>>>>>>>>> HTTP/1.1
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Often you want to query different amounts of data
> >>>>>>>>>>>>> depending of the case. Sometimes for example, person name
> >>>>>>>>>>>>> is enough, other time you want all the triples (DESCRIBE).
> >>>>>>>>>>>>> How do you specify here the context?
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> BTW is there a dedicated forum for discussing Linked Data
> >>>>>>>>>>>>> Templates?
> >>>>>>>>>>> --
> >>>>>>>>>>> Lingsoft - 30 years of Leading Language Management
> >>>>>>>>>>>
> >>>>>>>>>>> www.lingsoft.fi
> >>>>>>>>>>>
> >>>>>>>>>>> Speech Applications - Language Management - Translation -
> >>>>>>>>>>> Reader's and Writer's Tools - Text Tools - E-books and M-books
> >>>>>>>>>>>
> >>>>>>>>>>> Mikael Pesonen
> >>>>>>>>>>> System Engineer
> >>>>>>>>>>>
> >>>>>>>>>>> e-mail: mikael.pesonen@lingsoft.fi
> >>>>>>>>>>> Tel. +358 2 279 3300
> >>>>>>>>>>>
> >>>>>>>>>>> Time zone: GMT+2
> >>>>>>>>>>>
> >>>>>>>>>>> Helsinki Office
> >>>>>>>>>>> Eteläranta 10
> >>>>>>>>>>> FI-00130 Helsinki
> >>>>>>>>>>> FINLAND
> >>>>>>>>>>>
> >>>>>>>>>>> Turku Office
> >>>>>>>>>>> Kauppiaskatu 5 A
> >>>>>>>>>>> FI-20100 Turku
> >>>>>>>>>>> FINLAND
> >>>>>>>>>>>
> >>>>>>>>> --
> >>>>>>>>> Lingsoft - 30 years of Leading Language Management
> >>>>>>>>>
> >>>>>>>>> www.lingsoft.fi
> >>>>>>>>>
> >>>>>>>>> Speech Applications - Language Management - Translation -
> >>>>>>>>> Reader's and Writer's Tools - Text Tools - E-books and M-books
> >>>>>>>>>
> >>>>>>>>> Mikael Pesonen
> >>>>>>>>> System Engineer
> >>>>>>>>>
> >>>>>>>>> e-mail: mikael.pesonen@lingsoft.fi
> >>>>>>>>> Tel. +358 2 279 3300
> >>>>>>>>>
> >>>>>>>>> Time zone: GMT+2
> >>>>>>>>>
> >>>>>>>>> Helsinki Office
> >>>>>>>>> Eteläranta 10
> >>>>>>>>> FI-00130 Helsinki
> >>>>>>>>> FINLAND
> >>>>>>>>>
> >>>>>>>>> Turku Office
> >>>>>>>>> Kauppiaskatu 5 A
> >>>>>>>>> FI-20100 Turku
> >>>>>>>>> FINLAND
> >>>>>>>>>
> >>>>>>> --
> >>>>>>> Lingsoft - 30 years of Leading Language Management
> >>>>>>>
> >>>>>>> www.lingsoft.fi
> >>>>>>>
> >>>>>>> Speech Applications - Language Management - Translation -
> >>>>>>> Reader's and Writer's Tools - Text Tools - E-books and M-books
> >>>>>>>
> >>>>>>> Mikael Pesonen
> >>>>>>> System Engineer
> >>>>>>>
> >>>>>>> e-mail: mikael.pesonen@lingsoft.fi
> >>>>>>> Tel. +358 2 279 3300
> >>>>>>>
> >>>>>>> Time zone: GMT+2
> >>>>>>>
> >>>>>>> Helsinki Office
> >>>>>>> Eteläranta 10
> >>>>>>> FI-00130 Helsinki
> >>>>>>> FINLAND
> >>>>>>>
> >>>>>>> Turku Office
> >>>>>>> Kauppiaskatu 5 A
> >>>>>>> FI-20100 Turku
> >>>>>>> FINLAND
> >>>>>>>
> >>>>> --
> >>>>> Lingsoft - 30 years of Leading Language Management
> >>>>>
> >>>>> www.lingsoft.fi
> >>>>>
> >>>>> Speech Applications - Language Management - Translation - Reader's
> >>>>> and Writer's Tools - Text Tools - E-books and M-books
> >>>>>
> >>>>> Mikael Pesonen
> >>>>> System Engineer
> >>>>>
> >>>>> e-mail: mikael.pesonen@lingsoft.fi
> >>>>> Tel. +358 2 279 3300
> >>>>>
> >>>>> Time zone: GMT+2
> >>>>>
> >>>>> Helsinki Office
> >>>>> Eteläranta 10
> >>>>> FI-00130 Helsinki
> >>>>> FINLAND
> >>>>>
> >>>>> Turku Office
> >>>>> Kauppiaskatu 5 A
> >>>>> FI-20100 Turku
> >>>>> FINLAND
> >>>>>
> >>> --
> >>> Lingsoft - 30 years of Leading Language Management
> >>>
> >>> www.lingsoft.fi
> >>>
> >>> Speech Applications - Language Management - Translation - Reader's
> >>> and Writer's Tools - Text Tools - E-books and M-books
> >>>
> >>> Mikael Pesonen
> >>> System Engineer
> >>>
> >>> e-mail: mikael.pesonen@lingsoft.fi
> >>> Tel. +358 2 279 3300
> >>>
> >>> Time zone: GMT+2
> >>>
> >>> Helsinki Office
> >>> Eteläranta 10
> >>> FI-00130 Helsinki
> >>> FINLAND
> >>>
> >>> Turku Office
> >>> Kauppiaskatu 5 A
> >>> FI-20100 Turku
> >>> FINLAND
> >>>
> >
>
> --
> Lingsoft - 30 years of Leading Language Management
>
> www.lingsoft.fi
>
> Speech Applications - Language Management - Translation - Reader's and Writer's Tools - Text Tools - E-books and M-books
>
> Mikael Pesonen
> System Engineer
>
> e-mail: mikael.pesonen@lingsoft.fi
> Tel. +358 2 279 3300
>
> Time zone: GMT+2
>
> Helsinki Office
> Eteläranta 10
> FI-00130 Helsinki
> FINLAND
>
> Turku Office
> Kauppiaskatu 5 A
> FI-20100 Turku
> FINLAND
>

Received on Tuesday, 17 September 2019 11:57:50 UTC