From cb921ea2469c59c051e8777b315c3e8ae101a208 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Wed, 11 Sep 2024 15:40:03 +0200 Subject: [PATCH 01/43] Insert and upgrade SQL for text annotation --- install/1_structure.sql | 83 ++++++++++++++++++++++++++++- install/upgrade/text_annotation.sql | 34 ++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 install/upgrade/text_annotation.sql diff --git a/install/1_structure.sql b/install/1_structure.sql index 2b9a1f1b0..9cb64ef3c 100644 --- a/install/1_structure.sql +++ b/install/1_structure.sql @@ -2,8 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 15.6 (Debian 15.6-0+deb12u1) --- Dumped by pg_dump version 15.6 (Debian 15.6-0+deb12u1) +-- Dumped from database version 15.8 (Debian 15.8-0+deb12u1) +-- Dumped by pg_dump version 15.8 (Debian 15.8-0+deb12u1) SET statement_timeout = 0; SET lock_timeout = 0; @@ -32,6 +32,9 @@ ALTER TABLE IF EXISTS ONLY web.hierarchy DROP CONSTRAINT IF EXISTS hierarchy_id_ ALTER TABLE IF EXISTS ONLY web.hierarchy_openatlas_class DROP CONSTRAINT IF EXISTS hierarchy_form_hierarchy_id_fkey; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_image_id_fkey; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_entity_id_fkey; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_user_id_fkey; ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_image_id_fkey; ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_entity_id_fkey; @@ -67,6 +70,7 @@ DROP TRIGGER IF EXISTS update_modified ON web.i18n; DROP TRIGGER IF EXISTS update_modified ON web.hierarchy_openatlas_class; DROP TRIGGER IF EXISTS update_modified ON web.hierarchy; DROP TRIGGER IF EXISTS update_modified ON web."group"; +DROP TRIGGER IF EXISTS update_modified ON web.annotation_text; DROP TRIGGER IF EXISTS update_modified ON model.link; DROP TRIGGER IF EXISTS update_modified ON model.gis; DROP TRIGGER IF EXISTS update_modified ON model.file_info; @@ -104,6 +108,7 @@ ALTER TABLE IF EXISTS ONLY web."group" DROP CONSTRAINT IF EXISTS group_name_key; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_pkey; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_entity_id_key; ALTER TABLE IF EXISTS ONLY web.type_none_selectable DROP CONSTRAINT IF EXISTS entity_id_key; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_pkey; ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_pkey; ALTER TABLE IF EXISTS ONLY model.property DROP CONSTRAINT IF EXISTS property_pkey; ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_pkey; @@ -186,6 +191,8 @@ DROP SEQUENCE IF EXISTS web.group_id_seq; DROP TABLE IF EXISTS web."group"; DROP SEQUENCE IF EXISTS web.entity_profile_image_id_seq; DROP TABLE IF EXISTS web.entity_profile_image; +DROP TABLE IF EXISTS web.annotation_text; +DROP SEQUENCE IF EXISTS web.annotation_text_id_seq; DROP SEQUENCE IF EXISTS web.annotation_image_id_seq; DROP TABLE IF EXISTS web.annotation_image; DROP SEQUENCE IF EXISTS model.property_inheritance_id_seq; @@ -892,6 +899,39 @@ ALTER TABLE web.annotation_image_id_seq OWNER TO openatlas; ALTER SEQUENCE web.annotation_image_id_seq OWNED BY web.annotation_image.id; +-- +-- Name: annotation_text_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas +-- + +CREATE SEQUENCE web.annotation_text_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + MAXVALUE 2147483647 + CACHE 1; + + +ALTER TABLE web.annotation_text_id_seq OWNER TO openatlas; + +-- +-- Name: annotation_text; Type: TABLE; Schema: web; Owner: openatlas +-- + +CREATE TABLE web.annotation_text ( + id integer DEFAULT nextval('web.annotation_text_id_seq'::regclass) NOT NULL, + source_id integer NOT NULL, + entity_id integer NOT NULL, + link_start integer NOT NULL, + link_end integer NOT NULL, + user_id integer, + text integer, + created timestamp without time zone DEFAULT now() NOT NULL, + modified timestamp without time zone +); + + +ALTER TABLE web.annotation_text OWNER TO openatlas; + -- -- Name: entity_profile_image; Type: TABLE; Schema: web; Owner: openatlas -- @@ -1864,6 +1904,14 @@ ALTER TABLE ONLY web.annotation_image ADD CONSTRAINT annotation_image_pkey PRIMARY KEY (id); +-- +-- Name: annotation_text annotation_text_pkey; Type: CONSTRAINT; Schema: web; Owner: openatlas +-- + +ALTER TABLE ONLY web.annotation_text + ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); + + -- -- Name: type_none_selectable entity_id_key; Type: CONSTRAINT; Schema: web; Owner: openatlas -- @@ -2154,6 +2202,13 @@ CREATE TRIGGER update_modified BEFORE UPDATE ON model.gis FOR EACH ROW EXECUTE F CREATE TRIGGER update_modified BEFORE UPDATE ON model.link FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +-- +-- Name: annotation_text update_modified; Type: TRIGGER; Schema: web; Owner: openatlas +-- + +CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); + + -- -- Name: group update_modified; Type: TRIGGER; Schema: web; Owner: openatlas -- @@ -2424,6 +2479,30 @@ ALTER TABLE ONLY web.annotation_image ADD CONSTRAINT annotation_image_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE SET NULL; +-- +-- Name: annotation_text annotation_text_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas +-- + +ALTER TABLE ONLY web.annotation_text + ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: annotation_text annotation_text_source_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas +-- + +ALTER TABLE ONLY web.annotation_text + ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: annotation_text annotation_text_user_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas +-- + +ALTER TABLE ONLY web.annotation_text + ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; + + -- -- Name: entity_profile_image entity_profile_image_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas -- diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql new file mode 100644 index 000000000..56dcd4b47 --- /dev/null +++ b/install/upgrade/text_annotation.sql @@ -0,0 +1,34 @@ +BEGIN; + +-- #2079: Text annotation +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; +DROP TRIGGER IF EXISTS update_modified ON web.annotation_text; +ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_pkey; +DROP TABLE IF EXISTS web.annotation_text; +DROP SEQUENCE IF EXISTS web.annotation_text_id_seq; + +CREATE SEQUENCE web.annotation_text_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE MAXVALUE 2147483647 CACHE 1; +ALTER TABLE web.annotation_text_id_seq OWNER TO openatlas; + +CREATE TABLE web.annotation_text ( + id integer DEFAULT nextval('web.annotation_text_id_seq'::regclass) NOT NULL, + source_id integer NOT NULL, + entity_id integer NOT NULL, + link_start integer NOT NULL, + link_end integer NOT NULL, + user_id integer, + text integer, + created timestamp without time zone DEFAULT now() NOT NULL, + modified timestamp without time zone +); +ALTER TABLE web.annotation_text OWNER TO openatlas; + +ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); +CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; + +END; From b0207a70aa71c9dd6b5518754206c1098c7ee6b2 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Fri, 13 Sep 2024 16:49:36 +0200 Subject: [PATCH 02/43] Renaming annotation fields --- install/1_structure.sql | 2 +- install/upgrade/text_annotation.sql | 6 ++++++ openatlas/database/annotation.py | 10 +++++----- openatlas/models/annotation.py | 6 +++--- openatlas/views/admin.py | 2 +- openatlas/views/annotation.py | 2 +- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/install/1_structure.sql b/install/1_structure.sql index 9cb64ef3c..366460c8c 100644 --- a/install/1_structure.sql +++ b/install/1_structure.sql @@ -871,7 +871,7 @@ CREATE TABLE web.annotation_image ( entity_id integer, coordinates text NOT NULL, user_id integer, - annotation text NOT NULL, + text text, created timestamp without time zone DEFAULT now() NOT NULL ); diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql index 56dcd4b47..45e730b3c 100644 --- a/install/upgrade/text_annotation.sql +++ b/install/upgrade/text_annotation.sql @@ -1,6 +1,12 @@ BEGIN; -- #2079: Text annotation + +-- Adapt image annotation to sync fields +ALTER TABLE web.annotation_image RENAME COLUMN annotation TO text; +ALTER TABLE web.annotation_image ALTER COLUMN text DROP NOT NULL; + +-- Text annotation ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; diff --git a/openatlas/database/annotation.py b/openatlas/database/annotation.py index cc796b89f..f0754cd22 100644 --- a/openatlas/database/annotation.py +++ b/openatlas/database/annotation.py @@ -9,7 +9,7 @@ entity_id, coordinates, user_id, - annotation, + text, created FROM web.annotation_image """ @@ -36,7 +36,7 @@ def get_orphaned_annotations() -> list[dict[str, Any]]: a.entity_id, a.coordinates, a.user_id, - a.annotation, + a.text, a.created FROM web.annotation_image a LEFT JOIN model.link l ON l.domain_id = a.image_id @@ -55,13 +55,13 @@ def insert(data: dict[str, Any]) -> None: entity_id, coordinates, user_id, - annotation + text ) VALUES ( %(image_id)s, %(entity_id)s, %(coordinates)s, %(user_id)s, - %(annotation)s); + %(text)s); """, data) @@ -70,7 +70,7 @@ def update(data: dict[str, Any]) -> None: g.cursor.execute( """ UPDATE web.annotation_image - SET (entity_id, annotation) = (%(entity_id)s, %(annotation)s) + SET (entity_id, text) = (%(entity_id)s, %(text)s) WHERE id = %(id)s; """, data) diff --git a/openatlas/models/annotation.py b/openatlas/models/annotation.py index 5d5b507c1..95e8e5574 100644 --- a/openatlas/models/annotation.py +++ b/openatlas/models/annotation.py @@ -14,14 +14,14 @@ def __init__(self, data: dict[str, Any]) -> None: self.entity_id = data['entity_id'] self.coordinates = data['coordinates'] self.user_id = data['user_id'] - self.text = data['annotation'] + self.text = data['text'] self.created = data['created'] def update( self, entity_id: Optional[int] = None, text: Optional[str] = None) -> None: - db.update({'id': self.id, 'entity_id': entity_id, 'annotation': text}) + db.update({'id': self.id, 'entity_id': entity_id, 'text': text}) def delete(self) -> None: db.delete(self.id) @@ -55,4 +55,4 @@ def insert( 'user_id': current_user.id, 'entity_id': entity_id or None, 'coordinates': coordinates, - 'annotation': text}) + 'text': text}) diff --git a/openatlas/views/admin.py b/openatlas/views/admin.py index 96fc75b1c..353285594 100644 --- a/openatlas/views/admin.py +++ b/openatlas/views/admin.py @@ -485,7 +485,7 @@ def orphans() -> str: table=Table(['name', 'size', 'date', 'ext'])), 'orphaned_annotations': Tab( 'orphaned_annotations', - table=Table(['image', 'entity', 'annotation', 'creation'])), + table=Table(['image', 'entity', 'text', 'creation'])), 'orphaned_subunits': Tab( 'orphaned_subunits', table=Table([ diff --git a/openatlas/views/annotation.py b/openatlas/views/annotation.py index 8bb44fe4a..37800a39b 100644 --- a/openatlas/views/annotation.py +++ b/openatlas/views/annotation.py @@ -50,7 +50,7 @@ def annotation_insert(id_: int) -> str | Response: _('edit'), url_for('annotation_update', id_=annotation.id)), delete]) - table = Table(['date', 'annotation', 'entity'], rows, [[0, 'desc']]) + table = Table(['date', 'text', 'entity'], rows, [[0, 'desc']]) return render_template( 'tabs.html', tabs={ From a985b3626b111be281cf7ebd09bc7fe8e71d1017 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Mon, 16 Sep 2024 14:26:24 +0200 Subject: [PATCH 03/43] Adaption for annotation SQL --- install/1_structure.sql | 13 +++++++++++-- install/upgrade/text_annotation.sql | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/install/1_structure.sql b/install/1_structure.sql index 366460c8c..ec87b3417 100644 --- a/install/1_structure.sql +++ b/install/1_structure.sql @@ -71,6 +71,7 @@ DROP TRIGGER IF EXISTS update_modified ON web.hierarchy_openatlas_class; DROP TRIGGER IF EXISTS update_modified ON web.hierarchy; DROP TRIGGER IF EXISTS update_modified ON web."group"; DROP TRIGGER IF EXISTS update_modified ON web.annotation_text; +DROP TRIGGER IF EXISTS update_modified ON web.annotation_image; DROP TRIGGER IF EXISTS update_modified ON model.link; DROP TRIGGER IF EXISTS update_modified ON model.gis; DROP TRIGGER IF EXISTS update_modified ON model.file_info; @@ -872,7 +873,8 @@ CREATE TABLE web.annotation_image ( coordinates text NOT NULL, user_id integer, text text, - created timestamp without time zone DEFAULT now() NOT NULL + created timestamp without time zone DEFAULT now() NOT NULL, + modified timestamp without time zone ); @@ -920,7 +922,7 @@ ALTER TABLE web.annotation_text_id_seq OWNER TO openatlas; CREATE TABLE web.annotation_text ( id integer DEFAULT nextval('web.annotation_text_id_seq'::regclass) NOT NULL, source_id integer NOT NULL, - entity_id integer NOT NULL, + entity_id integer, link_start integer NOT NULL, link_end integer NOT NULL, user_id integer, @@ -2202,6 +2204,13 @@ CREATE TRIGGER update_modified BEFORE UPDATE ON model.gis FOR EACH ROW EXECUTE F CREATE TRIGGER update_modified BEFORE UPDATE ON model.link FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +-- +-- Name: annotation_image update_modified; Type: TRIGGER; Schema: web; Owner: openatlas +-- + +CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); + + -- -- Name: annotation_text update_modified; Type: TRIGGER; Schema: web; Owner: openatlas -- diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql index 45e730b3c..a181cdfa4 100644 --- a/install/upgrade/text_annotation.sql +++ b/install/upgrade/text_annotation.sql @@ -2,9 +2,11 @@ BEGIN; -- #2079: Text annotation --- Adapt image annotation to sync fields +-- Sync image annotation fields to text annotation fields ALTER TABLE web.annotation_image RENAME COLUMN annotation TO text; ALTER TABLE web.annotation_image ALTER COLUMN text DROP NOT NULL; +ALTER TABLE web.annotation_image ADD COLUMN modified timestamp without time zone; +CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- Text annotation ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; @@ -21,7 +23,7 @@ ALTER TABLE web.annotation_text_id_seq OWNER TO openatlas; CREATE TABLE web.annotation_text ( id integer DEFAULT nextval('web.annotation_text_id_seq'::regclass) NOT NULL, source_id integer NOT NULL, - entity_id integer NOT NULL, + entity_id integer, link_start integer NOT NULL, link_end integer NOT NULL, user_id integer, From b4f34687f035335f4d5ed59a5592659b76fe146c Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Mon, 16 Sep 2024 14:52:19 +0200 Subject: [PATCH 04/43] Renaming annotation functions --- openatlas/display/util.py | 2 +- openatlas/views/annotation.py | 24 +++++++++++++----------- tests/test_file.py | 12 ++++++------ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/openatlas/display/util.py b/openatlas/display/util.py index b26a0302a..4a64e0472 100644 --- a/openatlas/display/util.py +++ b/openatlas/display/util.py @@ -273,7 +273,7 @@ def profile_image(entity: Entity) -> str: if is_authorized('contributor'): html += (' - ' + link( _('annotate'), - url_for('annotation_insert', id_=file_id), + url_for('annotation_image_insert', id_=file_id), external=True)) if is_authorized('admin'): html += ('
' + link( diff --git a/openatlas/views/annotation.py b/openatlas/views/annotation.py index 37800a39b..b53e241a8 100644 --- a/openatlas/views/annotation.py +++ b/openatlas/views/annotation.py @@ -15,9 +15,9 @@ from openatlas.models.entity import Entity -@app.route('/annotation_insert/', methods=['GET', 'POST']) +@app.route('/annotation_image_insert/', methods=['GET', 'POST']) @required_group('contributor') -def annotation_insert(id_: int) -> str | Response: +def annotation_image_insert(id_: int) -> str | Response: image = Entity.get_by_id(id_, types=True, aliases=True) if not get_file_path(image.id): return abort(404) # pragma: no cover @@ -28,7 +28,7 @@ def annotation_insert(id_: int) -> str | Response: coordinates=form.coordinate.data, entity_id=form.entity.data, text=form.text.data) - return redirect(url_for('annotation_insert', id_=image.id)) + return redirect(url_for('annotation_image_insert', id_=image.id)) table = None if annotations := Annotation.get_by_file(image.id): rows = [] @@ -39,7 +39,7 @@ def annotation_insert(id_: int) -> str | Response: and current_user.id == annotation.user_id): delete = link( _('delete'), - url_for('annotation_delete', id_=annotation.id), + url_for('annotation_image_delete', id_=annotation.id), js="return confirm('" + _('delete annotation') + "?')") rows.append([ format_date(annotation.created), @@ -48,7 +48,7 @@ def annotation_insert(id_: int) -> str | Response: if annotation.entity_id else '', link( _('edit'), - url_for('annotation_update', id_=annotation.id)), + url_for('annotation_image_update', id_=annotation.id)), delete]) table = Table(['date', 'text', 'entity'], rows, [[0, 'desc']]) return render_template( @@ -67,9 +67,9 @@ def annotation_insert(id_: int) -> str | Response: _('annotate')]) -@app.route('/annotation_update/', methods=['GET', 'POST']) +@app.route('/annotation_image_update/', methods=['GET', 'POST']) @required_group('contributor') -def annotation_update(id_: int) -> str | Response: +def annotation_image_update(id_: int) -> str | Response: annotation = Annotation.get_by_id(id_) form = get_annotation_form( annotation.image_id, @@ -78,7 +78,8 @@ def annotation_update(id_: int) -> str | Response: insert=False) if form.validate_on_submit(): annotation.update(form.entity.data or None, form.text.data) - return redirect(url_for('annotation_insert', id_=annotation.image_id)) + return redirect( + url_for('annotation_image_insert', id_=annotation.image_id)) form.text.data = annotation.text form.entity.data = annotation.entity_id return render_template( @@ -90,13 +91,14 @@ def annotation_update(id_: int) -> str | Response: _('annotate')]) -@app.route('/annotation_delete/') +@app.route('/annotation_image_delete/') @required_group('contributor') -def annotation_delete(id_: int) -> Response: +def annotation_image_delete(id_: int) -> Response: annotation = Annotation.get_by_id(id_) if current_user.group == 'contributor' \ and annotation.user_id != current_user.id: abort(403) # pragma: no cover annotation.delete() flash(_('annotation deleted'), 'info') - return redirect(url_for('annotation_insert', id_=annotation.image_id)) + return redirect( + url_for('annotation_image_insert', id_=annotation.image_id)) diff --git a/tests/test_file.py b/tests/test_file.py index b78d25141..764a2d4b5 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -213,11 +213,11 @@ def test_file(self) -> None: rv = self.app.get(url_for('view', id_=place.id)) assert b'Logo' in rv.data - rv = self.app.get(url_for('annotation_insert', id_=iiif_id)) + rv = self.app.get(url_for('annotation_image_insert', id_=iiif_id)) assert b'annotate' in rv.data rv = self.app.post( - url_for('annotation_insert', id_=iiif_id), + url_for('annotation_image_insert', id_=iiif_id), data={ 'coordinate': '1.5,1.6,1.4,9.6,8.6,9.6,8.6,1.6', 'text': 'An interesting annotation', @@ -228,7 +228,7 @@ def test_file(self) -> None: rv = self.app.get(url_for('view_iiif', id_=iiif_id)) assert b'Mirador' in rv.data - rv = self.app.get(url_for('annotation_update', id_=1)) + rv = self.app.get(url_for('annotation_image_update', id_=1)) assert b'An interesting annotation' in rv.data rv = self.app.get( @@ -268,18 +268,18 @@ def test_file(self) -> None: assert b'Entity removed from annotation' in rv.data rv = self.app.post( - url_for('annotation_update', id_=1), + url_for('annotation_image_update', id_=1), data={'text': 'A boring annotation'}, follow_redirects=True) assert b'A boring annotation' in rv.data rv = self.app.get( - url_for('annotation_delete', id_=1), + url_for('annotation_image_delete', id_=1), follow_redirects=True) assert b'Annotation deleted' in rv.data self.app.post( - url_for('annotation_insert', id_=iiif_id), + url_for('annotation_image_insert', id_=iiif_id), data={ 'coordinate': '1.5,1.6,1.4,9.6,8.6,9.6,8.6,1.6', 'text': 'An interesting annotation', From a2d707936f49486a35fcf988dd8f23f920787616 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Mon, 16 Sep 2024 16:32:43 +0200 Subject: [PATCH 05/43] Text annotation functions --- openatlas/api/endpoints/iiif.py | 8 +- openatlas/database/annotation.py | 74 +++++++++++++--- openatlas/display/display.py | 6 ++ openatlas/forms/form.py | 22 ++++- openatlas/models/annotation.py | 84 ++++++++++++++++--- .../{annotate.html => annotate_image.html} | 0 openatlas/templates/annotate_text.html | 1 + openatlas/views/admin.py | 8 +- openatlas/views/annotation.py | 71 ++++++++++++++-- 9 files changed, 234 insertions(+), 40 deletions(-) rename openatlas/templates/{annotate.html => annotate_image.html} (100%) create mode 100644 openatlas/templates/annotate_text.html diff --git a/openatlas/api/endpoints/iiif.py b/openatlas/api/endpoints/iiif.py index b222e09dc..e32dfa308 100644 --- a/openatlas/api/endpoints/iiif.py +++ b/openatlas/api/endpoints/iiif.py @@ -10,7 +10,7 @@ from openatlas.api.resources.api_entity import ApiEntity from openatlas.api.resources.util import get_license_url, get_license_name -from openatlas.models.annotation import Annotation +from openatlas.models.annotation import AnnotationImage from openatlas.models.entity import Entity @@ -111,7 +111,7 @@ def get(image_id: int) -> Response: @staticmethod def build_annotation_list(image_id: int) -> dict[str, Any]: - annotations_ = Annotation.get_by_file(image_id) + annotations_ = AnnotationImage.get_by_file(image_id) return { "@context": "https://iiif.io/api/presentation/2/context.json", "@id": url_for( @@ -129,10 +129,10 @@ class IIIFAnnotation(Resource): def get(annotation_id: int) -> Response: return jsonify( IIIFAnnotation.build_annotation( - Annotation.get_by_id(annotation_id))) + AnnotationImage.get_by_id(annotation_id))) @staticmethod - def build_annotation(annotation: Annotation) -> dict[str, Any]: + def build_annotation(annotation: AnnotationImage) -> dict[str, Any]: entity_link = '' if annotation.entity_id: entity = ApiEntity.get_by_id(annotation.entity_id) diff --git a/openatlas/database/annotation.py b/openatlas/database/annotation.py index f0754cd22..dc4c00526 100644 --- a/openatlas/database/annotation.py +++ b/openatlas/database/annotation.py @@ -2,7 +2,8 @@ from flask import g -SELECT = """ +ANNOTATION_IMAGE_SELECT = \ + """ SELECT id, image_id, @@ -14,20 +15,50 @@ FROM web.annotation_image """ +ANNOTATION_TEXT_SELECT = \ + """ + SELECT + id, + source_id, + entity_id, + link_start, + link_end, + user_id, + text, + created + FROM web.annotation_text + """ -def get_by_id(id_: int) -> dict[str, Any]: - g.cursor.execute(SELECT + ' WHERE id = %(id)s;', {'id': id_}) + +def get_annotation_image_by_id(id_: int) -> dict[str, Any]: + g.cursor.execute( + ANNOTATION_IMAGE_SELECT + ' WHERE id = %(id)s;', + {'id': id_}) + return dict(g.cursor.fetchone()) if g.cursor.rowcount else {} + + +def get_annotation_text_by_id(id_: int) -> dict[str, Any]: + g.cursor.execute( + ANNOTATION_TEXT_SELECT + ' WHERE id = %(id)s;', + {'id': id_}) return dict(g.cursor.fetchone()) if g.cursor.rowcount else {} -def get_by_file(image_id: int) -> list[dict[str, Any]]: +def get_annotation_image_by_file(image_id: int) -> list[dict[str, Any]]: g.cursor.execute( - SELECT + ' WHERE image_id = %(image_id)s;', + ANNOTATION_IMAGE_SELECT + ' WHERE image_id = %(image_id)s;', {'image_id': image_id}) return [dict(row) for row in g.cursor.fetchall()] -def get_orphaned_annotations() -> list[dict[str, Any]]: +def get_annotation_text_by_source(source_id: int) -> list[dict[str, Any]]: + g.cursor.execute( + ANNOTATION_TEXT_SELECT + ' WHERE source_id = %(source_id)s;', + {'source_id': source_id}) + return [dict(row) for row in g.cursor.fetchall()] + + +def get_annotation_image_orphans() -> list[dict[str, Any]]: g.cursor.execute( """ SELECT @@ -47,7 +78,7 @@ def get_orphaned_annotations() -> list[dict[str, Any]]: return [dict(row) for row in g.cursor.fetchall()] -def insert(data: dict[str, Any]) -> None: +def insert_annotation_image(data: dict[str, Any]) -> None: g.cursor.execute( """ INSERT INTO web.annotation_image ( @@ -66,7 +97,7 @@ def insert(data: dict[str, Any]) -> None: data) -def update(data: dict[str, Any]) -> None: +def update_annotation_image(data: dict[str, Any]) -> None: g.cursor.execute( """ UPDATE web.annotation_image @@ -76,13 +107,15 @@ def update(data: dict[str, Any]) -> None: data) -def delete(id_: int) -> None: +def delete_annotation_image(id_: int) -> None: g.cursor.execute( 'DELETE FROM web.annotation_image WHERE id = %(id)s;', {'id': id_}) -def remove_entity(annotation_id: int, entity_id: int) -> None: +def remove_entity_from_annotation_image( + annotation_id: int, + entity_id: int) -> None: g.cursor.execute( """ UPDATE web.annotation_image @@ -90,3 +123,24 @@ def remove_entity(annotation_id: int, entity_id: int) -> None: WHERE id = %(annotation_id)s AND entity_id = %(entity_id)s; """, {'annotation_id': annotation_id, 'entity_id': entity_id}) + + +def insert_annotation_text(data: dict[str, Any]) -> None: + g.cursor.execute( + """ + INSERT INTO web.annotation_text ( + source_id, + entity_id, + link_start, + link_end, + user_id, + text + ) VALUES ( + %(source_id)s, + %(entity_id)s, + %(link_start)s, + %(link_end)s, + %(user_id)s, + %(text)s); + """, + data) diff --git a/openatlas/display/display.py b/openatlas/display/display.py index 3249ccf61..5780e6b84 100644 --- a/openatlas/display/display.py +++ b/openatlas/display/display.py @@ -288,6 +288,12 @@ class SourceDisplay(BaseDisplay): def add_button_network(self) -> None: pass + def add_button_others(self) -> None: + self.buttons.append( + button( + _('annotation'), + url_for('annotation_text_insert', id_=self.entity.id))) + def add_data(self) -> None: super().add_data() self.data[_('artifact')] = [ diff --git a/openatlas/forms/form.py b/openatlas/forms/form.py index 1dc3ad627..5dae6eb54 100644 --- a/openatlas/forms/form.py +++ b/openatlas/forms/form.py @@ -61,10 +61,10 @@ class Form(FlaskForm): return Form() -def get_annotation_form( +def get_annotation_image_form( image_id: int, entity: Optional[Entity] = None, - insert: Optional[bool] = True) -> FlaskForm: + insert: Optional[bool] = True) -> Any: class Form(FlaskForm): text = TextAreaField(_('annotation')) if insert: @@ -82,6 +82,24 @@ class Form(FlaskForm): return Form() +def get_annotation_text_form( + source_id: int, + entity: Optional[Entity] = None, + insert: Optional[bool] = True) -> Any: + class Form(FlaskForm): + text = TextAreaField(_('annotation')) + if insert: + pass + setattr( + Form, + 'entity', + TableField( + Entity.get_by_id(source_id).get_linked_entities('P67', sort=True), + entity)) + setattr(Form, 'save', SubmitField(_('save'))) + return Form() + + def get_table_form(classes: list[str], excluded: list[int]) -> str: entities = Entity.get_by_class(classes, types=True, aliases=True) table = Table([''] + g.table_headers[classes[0]], order=[[2, 'asc']]) diff --git a/openatlas/models/annotation.py b/openatlas/models/annotation.py index 95e8e5574..6519c502e 100644 --- a/openatlas/models/annotation.py +++ b/openatlas/models/annotation.py @@ -7,7 +7,7 @@ from openatlas.database import annotation as db -class Annotation: +class AnnotationImage: def __init__(self, data: dict[str, Any]) -> None: self.id = data['id'] self.image_id = data['image_id'] @@ -21,28 +21,34 @@ def update( self, entity_id: Optional[int] = None, text: Optional[str] = None) -> None: - db.update({'id': self.id, 'entity_id': entity_id, 'text': text}) + db.update_annotation_image({ + 'id': self.id, + 'entity_id': entity_id, + 'text': text}) def delete(self) -> None: - db.delete(self.id) + db.delete_annotation_image(self.id) @staticmethod - def get_by_id(id_: int) -> Annotation: - return Annotation(db.get_by_id(id_)) + def get_by_id(id_: int) -> AnnotationImage: + return AnnotationImage(db.get_annotation_image_by_id(id_)) @staticmethod - def get_by_file(image_id: int) -> list[Annotation]: - return [Annotation(row) for row in db.get_by_file(image_id)] + def get_by_file(image_id: int) -> list[AnnotationImage]: + return [ + AnnotationImage(row) for row + in db.get_annotation_image_by_file(image_id)] @staticmethod - def get_orphaned_annotations() -> list[Annotation]: - return [Annotation(row) for row in db.get_orphaned_annotations()] + def get_orphaned_annotations() -> list[AnnotationImage]: + return [ + AnnotationImage(row) for row in db.get_annotation_image_orphans()] @staticmethod def remove_entity_from_annotation( annotation_id: int, entity_id: int) -> None: - db.remove_entity(annotation_id, entity_id) + db.remove_entity_from_annotation_image(annotation_id, entity_id) @staticmethod def insert( @@ -50,9 +56,65 @@ def insert( coordinates: str, entity_id: Optional[int] = None, text: Optional[str] = None) -> None: - db.insert({ + db.insert_annotation_image({ 'image_id': image_id, 'user_id': current_user.id, 'entity_id': entity_id or None, 'coordinates': coordinates, 'text': text}) + + +class AnnotationText: + def __init__(self, data: dict[str, Any]) -> None: + self.id = data['id'] + self.source_id = data['source_id'] + self.entity_id = data['entity_id'] + self.link_start = data['link_start'] + self.link_start = data['link_end'] + self.user_id = data['user_id'] + self.text = data['text'] + self.created = data['created'] + + # def update( + # self, + # entity_id: Optional[int] = None, + # text: Optional[str] = None) -> None: + # db.update_annotation_image({ + # 'id': self.id, + # 'entity_id': entity_id, + # 'text': text}) + # + # def delete(self) -> None: + # db.delete_annotation_image(self.id) + # + # @staticmethod + # def get_by_id(id_: int) -> AnnotationText: + # return AnnotationText(db.get_annotation_image_by_id(id_)) + # + + @staticmethod + def get_by_source(source_id: int) -> list[AnnotationText]: + return [ + AnnotationText(row) for row + in db.get_annotation_text_by_source(source_id)] + # + # @staticmethod + # def remove_entity_from_annotation( + # annotation_id: int, + # entity_id: int) -> None: + # db.remove_entity_from_annotation_image(annotation_id, entity_id) + + @staticmethod + def insert( + source_id: int, + link_start: int, + link_end: int, + entity_id: Optional[int] = None, + text: Optional[str] = None) -> None: + db.insert_annotation_text({ + 'source_id': source_id, + 'user_id': current_user.id, + 'entity_id': entity_id or None, + 'link_start': link_start, + 'link_end': link_end, + 'text': text}) diff --git a/openatlas/templates/annotate.html b/openatlas/templates/annotate_image.html similarity index 100% rename from openatlas/templates/annotate.html rename to openatlas/templates/annotate_image.html diff --git a/openatlas/templates/annotate_text.html b/openatlas/templates/annotate_text.html new file mode 100644 index 000000000..6ebee3b68 --- /dev/null +++ b/openatlas/templates/annotate_text.html @@ -0,0 +1 @@ +Annotate text diff --git a/openatlas/views/admin.py b/openatlas/views/admin.py index 353285594..9822fa183 100644 --- a/openatlas/views/admin.py +++ b/openatlas/views/admin.py @@ -36,7 +36,7 @@ ApiForm, ContentForm, FrontendForm, GeneralForm, LogForm, MailForm, MapForm, ModulesForm, SimilarForm, TestMailForm) from openatlas.forms.util import get_form_settings, set_form_settings -from openatlas.models.annotation import Annotation +from openatlas.models.annotation import AnnotationImage from openatlas.models.checks import ( entities_linked_to_itself, invalid_cidoc_links, invalid_dates, orphaned_subunits, orphans as get_orphans, similar_named, @@ -563,7 +563,7 @@ def orphans() -> str: if is_authorized('editor') else '']) # Orphaned annotations - for annotation in Annotation.get_orphaned_annotations(): + for annotation in AnnotationImage.get_orphaned_annotations(): file = Entity.get_by_id(annotation.image_id) entity = Entity.get_by_id(annotation.entity_id) tabs['orphaned_annotations'].table.rows.append([ @@ -650,7 +650,7 @@ def admin_file_delete(filename: str) -> Response: @app.route('/admin/annotation/delete/') @required_group('editor') def admin_annotation_delete(id_: int) -> Response: - annotation = Annotation.get_by_id(id_) + annotation = AnnotationImage.get_by_id(id_) annotation.delete() flash(_('annotation deleted'), 'info') return redirect(f"{url_for('orphans')}#tab-orphaned-annotations") @@ -671,7 +671,7 @@ def admin_annotation_relink(image_id: int, entity_id: int) -> Response: def admin_annotation_remove_entity( annotation_id: int, entity_id: int) -> Response: - Annotation.remove_entity_from_annotation(annotation_id, entity_id) + AnnotationImage.remove_entity_from_annotation(annotation_id, entity_id) flash(_('entity removed from annotation'), 'info') return redirect(f"{url_for('orphans')}#tab-orphaned-annotations") diff --git a/openatlas/views/annotation.py b/openatlas/views/annotation.py index b53e241a8..8673b46d3 100644 --- a/openatlas/views/annotation.py +++ b/openatlas/views/annotation.py @@ -10,27 +10,80 @@ from openatlas.display.table import Table from openatlas.display.util import get_file_path, link, required_group from openatlas.display.util2 import format_date, is_authorized, manual -from openatlas.forms.form import get_annotation_form -from openatlas.models.annotation import Annotation +from openatlas.forms.form import ( + get_annotation_image_form, get_annotation_text_form) +from openatlas.models.annotation import AnnotationImage, AnnotationText from openatlas.models.entity import Entity +@app.route('/annotation_text_insert/', methods=['GET', 'POST']) +@required_group('contributor') +def annotation_text_insert(id_: int) -> str | Response: + source = Entity.get_by_id(id_, types=True) + form = get_annotation_text_form(source.id) + if form.validate_on_submit(): + AnnotationText.insert( + source_id=id_, + link_start=int(form.link_start.data), + link_end=int(form.entity.data), + text=form.text.data) + return redirect(url_for('annotation_text_insert', id_=source.id)) + table = None + if annotations := AnnotationText.get_by_source(source.id): + rows = [] + for annotation in annotations: + delete = '' + if is_authorized('editor') or ( + is_authorized('contributor') + and current_user.id == annotation.user_id): + pass + # delete = link( + # _('delete'), + # url_for('annotation_image_delete', id_=annotation.id), + # js="return confirm('" + _('delete annotation') + "?')") + rows.append([ + format_date(annotation.created), + annotation.text, + link(Entity.get_by_id(annotation.entity_id)) + if annotation.entity_id else '', + 'update', + # link( + # _('edit'), + # url_for('annotation_text_update', id_=annotation.id)), + delete]) + table = Table(['date', 'text', 'entity'], rows, [[0, 'desc']]) + return render_template( + 'tabs.html', + tabs={ + 'annotation': Tab( + 'annotation', + render_template('annotate_text.html', entity=source), + table, + [manual('tools/text_annotation')], + form=form)}, + entity=source, + crumbs=[ + [_('source'), url_for('index', view='source')], + source, + _('annotate')]) + + @app.route('/annotation_image_insert/', methods=['GET', 'POST']) @required_group('contributor') def annotation_image_insert(id_: int) -> str | Response: image = Entity.get_by_id(id_, types=True, aliases=True) if not get_file_path(image.id): return abort(404) # pragma: no cover - form = get_annotation_form(image.id) + form = get_annotation_image_form(image.id) if form.validate_on_submit(): - Annotation.insert( + AnnotationImage.insert( image_id=id_, coordinates=form.coordinate.data, entity_id=form.entity.data, text=form.text.data) return redirect(url_for('annotation_image_insert', id_=image.id)) table = None - if annotations := Annotation.get_by_file(image.id): + if annotations := AnnotationImage.get_by_file(image.id): rows = [] for annotation in annotations: delete = '' @@ -56,7 +109,7 @@ def annotation_image_insert(id_: int) -> str | Response: tabs={ 'annotation': Tab( 'annotation', - render_template('annotate.html', entity=image), + render_template('annotate_image.html', entity=image), table, [manual('tools/image_annotation')], form=form)}, @@ -70,8 +123,8 @@ def annotation_image_insert(id_: int) -> str | Response: @app.route('/annotation_image_update/', methods=['GET', 'POST']) @required_group('contributor') def annotation_image_update(id_: int) -> str | Response: - annotation = Annotation.get_by_id(id_) - form = get_annotation_form( + annotation = AnnotationImage.get_by_id(id_) + form = get_annotation_image_form( annotation.image_id, Entity.get_by_id(annotation.entity_id) if annotation.entity_id else None, @@ -94,7 +147,7 @@ def annotation_image_update(id_: int) -> str | Response: @app.route('/annotation_image_delete/') @required_group('contributor') def annotation_image_delete(id_: int) -> Response: - annotation = Annotation.get_by_id(id_) + annotation = AnnotationImage.get_by_id(id_) if current_user.group == 'contributor' \ and annotation.user_id != current_user.id: abort(403) # pragma: no cover From eedbb57dff44c8a844ba9b0553a799564569fd1f Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Wed, 2 Oct 2024 15:36:22 +0200 Subject: [PATCH 06/43] Text annotation insert and update --- install/1_structure.sql | 2 +- install/upgrade/text_annotation.sql | 2 +- openatlas/forms/form.py | 6 ++++ openatlas/models/annotation.py | 16 +++++------ openatlas/templates/annotate_text.html | 2 +- openatlas/views/annotation.py | 40 ++++++++++++++++++++++---- 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/install/1_structure.sql b/install/1_structure.sql index ec87b3417..5d3e95b20 100644 --- a/install/1_structure.sql +++ b/install/1_structure.sql @@ -926,7 +926,7 @@ CREATE TABLE web.annotation_text ( link_start integer NOT NULL, link_end integer NOT NULL, user_id integer, - text integer, + text text, created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone ); diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql index a181cdfa4..012f56e0d 100644 --- a/install/upgrade/text_annotation.sql +++ b/install/upgrade/text_annotation.sql @@ -27,7 +27,7 @@ CREATE TABLE web.annotation_text ( link_start integer NOT NULL, link_end integer NOT NULL, user_id integer, - text integer, + text text, created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone ); diff --git a/openatlas/forms/form.py b/openatlas/forms/form.py index 5dae6eb54..f8c6ede1f 100644 --- a/openatlas/forms/form.py +++ b/openatlas/forms/form.py @@ -7,6 +7,7 @@ from flask_login import current_user from flask_wtf import FlaskForm from wtforms import HiddenField, SelectMultipleField, StringField, widgets +from wtforms.fields.numeric import IntegerField from wtforms.fields.simple import TextAreaField from wtforms.validators import InputRequired, URL @@ -86,10 +87,15 @@ def get_annotation_text_form( source_id: int, entity: Optional[Entity] = None, insert: Optional[bool] = True) -> Any: + class Form(FlaskForm): text = TextAreaField(_('annotation')) + link_start = IntegerField() + link_end = IntegerField() + if insert: pass + setattr( Form, 'entity', diff --git a/openatlas/models/annotation.py b/openatlas/models/annotation.py index 6519c502e..0854b7781 100644 --- a/openatlas/models/annotation.py +++ b/openatlas/models/annotation.py @@ -70,7 +70,7 @@ def __init__(self, data: dict[str, Any]) -> None: self.source_id = data['source_id'] self.entity_id = data['entity_id'] self.link_start = data['link_start'] - self.link_start = data['link_end'] + self.link_end = data['link_end'] self.user_id = data['user_id'] self.text = data['text'] self.created = data['created'] @@ -87,10 +87,10 @@ def __init__(self, data: dict[str, Any]) -> None: # def delete(self) -> None: # db.delete_annotation_image(self.id) # - # @staticmethod - # def get_by_id(id_: int) -> AnnotationText: - # return AnnotationText(db.get_annotation_image_by_id(id_)) - # + @staticmethod + def get_by_id(id_: int) -> AnnotationText: + return AnnotationText(db.get_annotation_text_by_id(id_)) + @staticmethod def get_by_source(source_id: int) -> list[AnnotationText]: @@ -113,8 +113,8 @@ def insert( text: Optional[str] = None) -> None: db.insert_annotation_text({ 'source_id': source_id, - 'user_id': current_user.id, - 'entity_id': entity_id or None, 'link_start': link_start, 'link_end': link_end, - 'text': text}) + 'entity_id': entity_id, + 'text': text, + 'user_id': current_user.id,}) diff --git a/openatlas/templates/annotate_text.html b/openatlas/templates/annotate_text.html index 6ebee3b68..9ac7f6e95 100644 --- a/openatlas/templates/annotate_text.html +++ b/openatlas/templates/annotate_text.html @@ -1 +1 @@ -Annotate text +{{ formatted_text }} diff --git a/openatlas/views/annotation.py b/openatlas/views/annotation.py index 8673b46d3..6043215e0 100644 --- a/openatlas/views/annotation.py +++ b/openatlas/views/annotation.py @@ -25,7 +25,7 @@ def annotation_text_insert(id_: int) -> str | Response: AnnotationText.insert( source_id=id_, link_start=int(form.link_start.data), - link_end=int(form.entity.data), + link_end=int(form.link_end.data), text=form.text.data) return redirect(url_for('annotation_text_insert', id_=source.id)) table = None @@ -47,9 +47,9 @@ def annotation_text_insert(id_: int) -> str | Response: link(Entity.get_by_id(annotation.entity_id)) if annotation.entity_id else '', 'update', - # link( - # _('edit'), - # url_for('annotation_text_update', id_=annotation.id)), + link( + _('edit'), + url_for('annotation_text_update', id_=annotation.id)), delete]) table = Table(['date', 'text', 'entity'], rows, [[0, 'desc']]) return render_template( @@ -57,7 +57,10 @@ def annotation_text_insert(id_: int) -> str | Response: tabs={ 'annotation': Tab( 'annotation', - render_template('annotate_text.html', entity=source), + render_template( + 'annotate_text.html', + entity=source, + formatted_text=source.description), table, [manual('tools/text_annotation')], form=form)}, @@ -68,6 +71,33 @@ def annotation_text_insert(id_: int) -> str | Response: _('annotate')]) +@app.route('/annotation_text_update/', methods=['GET', 'POST']) +@required_group('contributor') +def annotation_text_update(id_: int) -> str | Response: + annotation = AnnotationText.get_by_id(id_) + source = Entity.get_by_id(annotation.source_id) + form = get_annotation_text_form( + annotation.source_id, + Entity.get_by_id(annotation.entity_id) + if annotation.entity_id else None, + insert=False) + if form.validate_on_submit(): + #annotation.update(form.entity.data or None, form.text.data) + return redirect(url_for('annotation_text_insert', id_=source.id)) + print(annotation.link_start) + form.text.data = annotation.text + form.entity.data = annotation.entity_id + form.link_start.data = annotation.link_start + form.link_end.data = annotation.link_end + return render_template( + 'tabs.html', + tabs={'annotation': Tab('annotation', form=form)}, + crumbs=[ + [_('source'), url_for('index', view='source')], + source, + _('annotate')]) + + @app.route('/annotation_image_insert/', methods=['GET', 'POST']) @required_group('contributor') def annotation_image_insert(id_: int) -> str | Response: From 896b7ebb2c7ebb64008e8f60010936ce73bf6d6a Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Thu, 3 Oct 2024 15:38:16 +0200 Subject: [PATCH 07/43] Text annotation: update and delete --- openatlas/database/annotation.py | 18 ++++++++++++++ openatlas/models/annotation.py | 37 +++++++++++++--------------- openatlas/views/annotation.py | 41 +++++++++++++++++++++++--------- 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/openatlas/database/annotation.py b/openatlas/database/annotation.py index dc4c00526..6c4707eb3 100644 --- a/openatlas/database/annotation.py +++ b/openatlas/database/annotation.py @@ -113,6 +113,24 @@ def delete_annotation_image(id_: int) -> None: {'id': id_}) +def update_annotation_text(data: dict[str, Any]) -> None: + print(data) + g.cursor.execute( + """ + UPDATE web.annotation_text + SET (entity_id, text, link_start, link_end) = + (%(entity_id)s, %(text)s, %(link_start)s, %(link_end)s) + WHERE id = %(id)s; + """, + data) + + +def delete_annotation_text(id_: int) -> None: + g.cursor.execute( + 'DELETE FROM web.annotation_text WHERE id = %(id)s;', + {'id': id_}) + + def remove_entity_from_annotation_image( annotation_id: int, entity_id: int) -> None: diff --git a/openatlas/models/annotation.py b/openatlas/models/annotation.py index 0854b7781..9e4a1e2cd 100644 --- a/openatlas/models/annotation.py +++ b/openatlas/models/annotation.py @@ -75,34 +75,31 @@ def __init__(self, data: dict[str, Any]) -> None: self.text = data['text'] self.created = data['created'] - # def update( - # self, - # entity_id: Optional[int] = None, - # text: Optional[str] = None) -> None: - # db.update_annotation_image({ - # 'id': self.id, - # 'entity_id': entity_id, - # 'text': text}) - # - # def delete(self) -> None: - # db.delete_annotation_image(self.id) - # + def update( + self, + link_start: int, + link_end: int, + entity_id: Optional[int] = None, + text: Optional[str] = None) -> None: + db.update_annotation_text({ + 'id': self.id, + 'entity_id': entity_id, + 'text': text, + 'link_start': link_start, + 'link_end': link_end}) + + def delete(self) -> None: + db.delete_annotation_text(self.id) + @staticmethod def get_by_id(id_: int) -> AnnotationText: return AnnotationText(db.get_annotation_text_by_id(id_)) - @staticmethod def get_by_source(source_id: int) -> list[AnnotationText]: return [ AnnotationText(row) for row in db.get_annotation_text_by_source(source_id)] - # - # @staticmethod - # def remove_entity_from_annotation( - # annotation_id: int, - # entity_id: int) -> None: - # db.remove_entity_from_annotation_image(annotation_id, entity_id) @staticmethod def insert( @@ -117,4 +114,4 @@ def insert( 'link_end': link_end, 'entity_id': entity_id, 'text': text, - 'user_id': current_user.id,}) + 'user_id': current_user.id}) diff --git a/openatlas/views/annotation.py b/openatlas/views/annotation.py index 6043215e0..321ba4079 100644 --- a/openatlas/views/annotation.py +++ b/openatlas/views/annotation.py @@ -30,28 +30,31 @@ def annotation_text_insert(id_: int) -> str | Response: return redirect(url_for('annotation_text_insert', id_=source.id)) table = None if annotations := AnnotationText.get_by_source(source.id): - rows = [] + table = Table( + ['date', 'text', 'entity', 'start', 'end'], + [], + [[0, 'desc']]) for annotation in annotations: delete = '' if is_authorized('editor') or ( is_authorized('contributor') and current_user.id == annotation.user_id): - pass - # delete = link( - # _('delete'), - # url_for('annotation_image_delete', id_=annotation.id), - # js="return confirm('" + _('delete annotation') + "?')") - rows.append([ + delete = link( + _('delete'), + url_for('annotation_text_delete', id_=annotation.id), + js="return confirm('" + _('delete annotation') + "?')") + table.rows.append([ format_date(annotation.created), annotation.text, link(Entity.get_by_id(annotation.entity_id)) if annotation.entity_id else '', - 'update', + annotation.link_start, + annotation.link_end, link( _('edit'), url_for('annotation_text_update', id_=annotation.id)), delete]) - table = Table(['date', 'text', 'entity'], rows, [[0, 'desc']]) + return render_template( 'tabs.html', tabs={ @@ -82,9 +85,12 @@ def annotation_text_update(id_: int) -> str | Response: if annotation.entity_id else None, insert=False) if form.validate_on_submit(): - #annotation.update(form.entity.data or None, form.text.data) + annotation.update( + form.link_start.data, + form.link_end.data, + int(form.entity.data) if form.entity.data else None, + form.text.data) return redirect(url_for('annotation_text_insert', id_=source.id)) - print(annotation.link_start) form.text.data = annotation.text form.entity.data = annotation.entity_id form.link_start.data = annotation.link_start @@ -185,3 +191,16 @@ def annotation_image_delete(id_: int) -> Response: flash(_('annotation deleted'), 'info') return redirect( url_for('annotation_image_insert', id_=annotation.image_id)) + + +@app.route('/annotation_text_delete/') +@required_group('contributor') +def annotation_text_delete(id_: int) -> Response: + annotation = AnnotationText.get_by_id(id_) + if current_user.group == 'contributor' \ + and annotation.user_id != current_user.id: + abort(403) # pragma: no cover + annotation.delete() + flash(_('annotation deleted'), 'info') + return redirect( + url_for('annotation_text_insert', id_=annotation.source_id)) From 249fc0d78239341b513023a0e1189b7c661fe9d5 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Thu, 3 Oct 2024 17:46:51 +0200 Subject: [PATCH 08/43] Text annotation: display annotation links --- openatlas/database/annotation.py | 6 +++++- openatlas/display/util.py | 19 +++++++++++++++++++ openatlas/models/annotation.py | 2 +- openatlas/templates/annotate_text.html | 2 +- openatlas/views/annotation.py | 8 +++++--- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/openatlas/database/annotation.py b/openatlas/database/annotation.py index 6c4707eb3..b4be486ce 100644 --- a/openatlas/database/annotation.py +++ b/openatlas/database/annotation.py @@ -53,7 +53,11 @@ def get_annotation_image_by_file(image_id: int) -> list[dict[str, Any]]: def get_annotation_text_by_source(source_id: int) -> list[dict[str, Any]]: g.cursor.execute( - ANNOTATION_TEXT_SELECT + ' WHERE source_id = %(source_id)s;', + ANNOTATION_TEXT_SELECT + + """ + WHERE source_id = %(source_id)s + ORDER BY link_start; + """, {'source_id': source_id}) return [dict(row) for row in g.cursor.fetchall()] diff --git a/openatlas/display/util.py b/openatlas/display/util.py index 4a64e0472..d155a2fa3 100644 --- a/openatlas/display/util.py +++ b/openatlas/display/util.py @@ -21,6 +21,7 @@ from openatlas import app from openatlas.display.image_processing import check_processed_image from openatlas.display.util2 import format_date, is_authorized, uc_first +from openatlas.models.annotation import AnnotationText from openatlas.models.cidoc_class import CidocClass from openatlas.models.cidoc_property import CidocProperty from openatlas.models.content import get_translation @@ -641,3 +642,21 @@ def convert_image_to_iiif(id_: int, path: Optional[Path] = None) -> bool: except Exception: # pragma: no cover return False return True + + +def display_annotation_text_links(source: Entity) -> str: + offset = 0 + text = source.description + for annotation in AnnotationText.get_by_source_id(source.id): + if annotation.entity_id: + tag_open = f'' + tag_close = '' + + position = annotation.link_start + offset + text = text[:position] + tag_open + text[position:] + offset += len(tag_open) + + position = annotation.link_end + offset + text = text[:position] + tag_close + text[position:] + offset += len(tag_close) + return text + '

' + source.description diff --git a/openatlas/models/annotation.py b/openatlas/models/annotation.py index 9e4a1e2cd..58b0f688e 100644 --- a/openatlas/models/annotation.py +++ b/openatlas/models/annotation.py @@ -96,7 +96,7 @@ def get_by_id(id_: int) -> AnnotationText: return AnnotationText(db.get_annotation_text_by_id(id_)) @staticmethod - def get_by_source(source_id: int) -> list[AnnotationText]: + def get_by_source_id(source_id: int) -> list[AnnotationText]: return [ AnnotationText(row) for row in db.get_annotation_text_by_source(source_id)] diff --git a/openatlas/templates/annotate_text.html b/openatlas/templates/annotate_text.html index 9ac7f6e95..1a7f81d27 100644 --- a/openatlas/templates/annotate_text.html +++ b/openatlas/templates/annotate_text.html @@ -1 +1 @@ -{{ formatted_text }} +{{ formatted_text|safe }} diff --git a/openatlas/views/annotation.py b/openatlas/views/annotation.py index 321ba4079..101ab5575 100644 --- a/openatlas/views/annotation.py +++ b/openatlas/views/annotation.py @@ -8,7 +8,8 @@ from openatlas import app from openatlas.display.tab import Tab from openatlas.display.table import Table -from openatlas.display.util import get_file_path, link, required_group +from openatlas.display.util import ( + display_annotation_text_links, get_file_path, link, required_group) from openatlas.display.util2 import format_date, is_authorized, manual from openatlas.forms.form import ( get_annotation_image_form, get_annotation_text_form) @@ -26,10 +27,11 @@ def annotation_text_insert(id_: int) -> str | Response: source_id=id_, link_start=int(form.link_start.data), link_end=int(form.link_end.data), + entity_id=int(form.entity.data) if form.entity.data else None, text=form.text.data) return redirect(url_for('annotation_text_insert', id_=source.id)) table = None - if annotations := AnnotationText.get_by_source(source.id): + if annotations := AnnotationText.get_by_source_id(source.id): table = Table( ['date', 'text', 'entity', 'start', 'end'], [], @@ -63,7 +65,7 @@ def annotation_text_insert(id_: int) -> str | Response: render_template( 'annotate_text.html', entity=source, - formatted_text=source.description), + formatted_text=display_annotation_text_links(source)), table, [manual('tools/text_annotation')], form=form)}, From 8945f8f8d17bd237b0e60246d8a8277d5946211e Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Sat, 5 Oct 2024 12:12:26 +0200 Subject: [PATCH 09/43] Text annotation: display annotation text --- openatlas/display/util.py | 26 +++++++++++++++++--------- openatlas/templates/annotate_text.html | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/openatlas/display/util.py b/openatlas/display/util.py index d155a2fa3..033b87eba 100644 --- a/openatlas/display/util.py +++ b/openatlas/display/util.py @@ -20,7 +20,8 @@ from openatlas import app from openatlas.display.image_processing import check_processed_image -from openatlas.display.util2 import format_date, is_authorized, uc_first +from openatlas.display.util2 import ( + format_date, is_authorized, sanitize, uc_first) from openatlas.models.annotation import AnnotationText from openatlas.models.cidoc_class import CidocClass from openatlas.models.cidoc_property import CidocProperty @@ -648,15 +649,22 @@ def display_annotation_text_links(source: Entity) -> str: offset = 0 text = source.description for annotation in AnnotationText.get_by_source_id(source.id): + if not annotation.text and not annotation.entity_id: + continue + title = f'title="{sanitize(annotation.text, "text")}"' \ + if annotation.text else '' if annotation.entity_id: - tag_open = f'' + tag_open = f'' tag_close = '' + else: + tag_open = f'' + tag_close = '' - position = annotation.link_start + offset - text = text[:position] + tag_open + text[position:] - offset += len(tag_open) + position = annotation.link_start + offset + text = text[:position] + tag_open + text[position:] + offset += len(tag_open) - position = annotation.link_end + offset - text = text[:position] + tag_close + text[position:] - offset += len(tag_close) - return text + '

' + source.description + position = annotation.link_end + offset + text = text[:position] + tag_close + text[position:] + offset += len(tag_close) + return text diff --git a/openatlas/templates/annotate_text.html b/openatlas/templates/annotate_text.html index 1a7f81d27..9c85ebfd0 100644 --- a/openatlas/templates/annotate_text.html +++ b/openatlas/templates/annotate_text.html @@ -1 +1,2 @@ +

Backend prototype for text annotation

{{ formatted_text|safe }} From 0924b7c464975395ebf318aadf255005d35f8f61 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Tue, 8 Oct 2024 13:54:15 +0200 Subject: [PATCH 10/43] Moved annotation tables to model schema --- install/1_structure.sql | 395 +++++++++++++++------------- install/upgrade/text_annotation.sql | 43 +-- openatlas/database/annotation.py | 20 +- 3 files changed, 239 insertions(+), 219 deletions(-) diff --git a/install/1_structure.sql b/install/1_structure.sql index 5d3e95b20..f60de97ed 100644 --- a/install/1_structure.sql +++ b/install/1_structure.sql @@ -32,12 +32,6 @@ ALTER TABLE IF EXISTS ONLY web.hierarchy DROP CONSTRAINT IF EXISTS hierarchy_id_ ALTER TABLE IF EXISTS ONLY web.hierarchy_openatlas_class DROP CONSTRAINT IF EXISTS hierarchy_form_hierarchy_id_fkey; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_image_id_fkey; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_entity_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_user_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_image_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_entity_id_fkey; ALTER TABLE IF EXISTS ONLY model.property DROP CONSTRAINT IF EXISTS property_range_class_code_fkey; ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_super_code_fkey; ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_sub_code_fkey; @@ -57,6 +51,12 @@ ALTER TABLE IF EXISTS ONLY model.entity DROP CONSTRAINT IF EXISTS entity_class_c ALTER TABLE IF EXISTS ONLY model.cidoc_class_inheritance DROP CONSTRAINT IF EXISTS class_inheritance_super_code_fkey; ALTER TABLE IF EXISTS ONLY model.cidoc_class_inheritance DROP CONSTRAINT IF EXISTS class_inheritance_sub_code_fkey; ALTER TABLE IF EXISTS ONLY model.cidoc_class_i18n DROP CONSTRAINT IF EXISTS class_i18n_class_code_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_user_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_image_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_entity_id_fkey; ALTER TABLE IF EXISTS ONLY import.entity DROP CONSTRAINT IF EXISTS entity_user_id_fkey; ALTER TABLE IF EXISTS ONLY import.entity DROP CONSTRAINT IF EXISTS entity_project_id_fkey; ALTER TABLE IF EXISTS ONLY import.entity DROP CONSTRAINT IF EXISTS entity_entity_id_fkey; @@ -70,12 +70,12 @@ DROP TRIGGER IF EXISTS update_modified ON web.i18n; DROP TRIGGER IF EXISTS update_modified ON web.hierarchy_openatlas_class; DROP TRIGGER IF EXISTS update_modified ON web.hierarchy; DROP TRIGGER IF EXISTS update_modified ON web."group"; -DROP TRIGGER IF EXISTS update_modified ON web.annotation_text; -DROP TRIGGER IF EXISTS update_modified ON web.annotation_image; DROP TRIGGER IF EXISTS update_modified ON model.link; DROP TRIGGER IF EXISTS update_modified ON model.gis; DROP TRIGGER IF EXISTS update_modified ON model.file_info; DROP TRIGGER IF EXISTS update_modified ON model.entity; +DROP TRIGGER IF EXISTS update_modified ON model.annotation_text; +DROP TRIGGER IF EXISTS update_modified ON model.annotation_image; DROP TRIGGER IF EXISTS on_delete_entity ON model.entity; DROP TRIGGER IF EXISTS update_modified ON import.project; ALTER TABLE IF EXISTS ONLY web."user" DROP CONSTRAINT IF EXISTS user_username_key; @@ -109,8 +109,6 @@ ALTER TABLE IF EXISTS ONLY web."group" DROP CONSTRAINT IF EXISTS group_name_key; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_pkey; ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_entity_id_key; ALTER TABLE IF EXISTS ONLY web.type_none_selectable DROP CONSTRAINT IF EXISTS entity_id_key; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_pkey; -ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_pkey; ALTER TABLE IF EXISTS ONLY model.property DROP CONSTRAINT IF EXISTS property_pkey; ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_pkey; ALTER TABLE IF EXISTS ONLY model.property_i18n DROP CONSTRAINT IF EXISTS property_i18n_property_code_language_code_key; @@ -130,6 +128,8 @@ ALTER TABLE IF EXISTS ONLY model.cidoc_class_inheritance DROP CONSTRAINT IF EXIS ALTER TABLE IF EXISTS ONLY model.cidoc_class_i18n DROP CONSTRAINT IF EXISTS class_i18n_pkey; ALTER TABLE IF EXISTS ONLY model.cidoc_class_i18n DROP CONSTRAINT IF EXISTS class_i18n_class_code_language_code_key; ALTER TABLE IF EXISTS ONLY model.cidoc_class DROP CONSTRAINT IF EXISTS class_code_key; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_pkey; +ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_pkey; ALTER TABLE IF EXISTS ONLY import.project DROP CONSTRAINT IF EXISTS project_pkey; ALTER TABLE IF EXISTS ONLY import.project DROP CONSTRAINT IF EXISTS project_name_key; ALTER TABLE IF EXISTS ONLY import.entity DROP CONSTRAINT IF EXISTS entity_project_id_origin_id_key; @@ -139,6 +139,7 @@ ALTER TABLE IF EXISTS web.user_notes ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web.user_log ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web.user_bookmarks ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web."user" ALTER COLUMN id DROP DEFAULT; +ALTER TABLE IF EXISTS web.type_none_selectable ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web.system_log ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web.settings ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web.reference_system_openatlas_class ALTER COLUMN id DROP DEFAULT; @@ -148,7 +149,6 @@ ALTER TABLE IF EXISTS web.hierarchy_openatlas_class ALTER COLUMN id DROP DEFAULT ALTER TABLE IF EXISTS web.hierarchy ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web."group" ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS web.entity_profile_image ALTER COLUMN id DROP DEFAULT; -ALTER TABLE IF EXISTS web.annotation_image ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS model.property_inheritance ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS model.property_i18n ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS model.property ALTER COLUMN id DROP DEFAULT; @@ -159,6 +159,7 @@ ALTER TABLE IF EXISTS model.entity ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS model.cidoc_class_inheritance ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS model.cidoc_class_i18n ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS model.cidoc_class ALTER COLUMN id DROP DEFAULT; +ALTER TABLE IF EXISTS model.annotation_image ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS import.project ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS import.entity ALTER COLUMN id DROP DEFAULT; DROP SEQUENCE IF EXISTS web.user_settings_id_seq; @@ -171,8 +172,8 @@ DROP SEQUENCE IF EXISTS web.user_id_seq; DROP SEQUENCE IF EXISTS web.user_bookmarks_id_seq; DROP TABLE IF EXISTS web.user_bookmarks; DROP TABLE IF EXISTS web."user"; -DROP TABLE IF EXISTS web.type_none_selectable; DROP SEQUENCE IF EXISTS web.type_none_selectable_id_seq; +DROP TABLE IF EXISTS web.type_none_selectable; DROP SEQUENCE IF EXISTS web.settings_id_seq; DROP TABLE IF EXISTS web.settings; DROP SEQUENCE IF EXISTS web.reference_system_form_id_seq; @@ -192,10 +193,6 @@ DROP SEQUENCE IF EXISTS web.group_id_seq; DROP TABLE IF EXISTS web."group"; DROP SEQUENCE IF EXISTS web.entity_profile_image_id_seq; DROP TABLE IF EXISTS web.entity_profile_image; -DROP TABLE IF EXISTS web.annotation_text; -DROP SEQUENCE IF EXISTS web.annotation_text_id_seq; -DROP SEQUENCE IF EXISTS web.annotation_image_id_seq; -DROP TABLE IF EXISTS web.annotation_image; DROP SEQUENCE IF EXISTS model.property_inheritance_id_seq; DROP TABLE IF EXISTS model.property_inheritance; DROP SEQUENCE IF EXISTS model.property_id_seq; @@ -218,6 +215,10 @@ DROP SEQUENCE IF EXISTS model.cidoc_class_id_seq; DROP SEQUENCE IF EXISTS model.cidoc_class_i18n_id_seq; DROP TABLE IF EXISTS model.cidoc_class_i18n; DROP TABLE IF EXISTS model.cidoc_class; +DROP TABLE IF EXISTS model.annotation_text; +DROP SEQUENCE IF EXISTS model.annotation_text_id_seq; +DROP SEQUENCE IF EXISTS model.annotation_image_id_seq; +DROP TABLE IF EXISTS model.annotation_image; DROP SEQUENCE IF EXISTS import.project_id_seq; DROP TABLE IF EXISTS import.project; DROP SEQUENCE IF EXISTS import.entity_id_seq; @@ -403,6 +404,78 @@ ALTER TABLE import.project_id_seq OWNER TO openatlas; ALTER SEQUENCE import.project_id_seq OWNED BY import.project.id; +-- +-- Name: annotation_image; Type: TABLE; Schema: model; Owner: openatlas +-- + +CREATE TABLE model.annotation_image ( + id integer NOT NULL, + image_id integer NOT NULL, + entity_id integer, + coordinates text NOT NULL, + user_id integer, + text text, + created timestamp without time zone DEFAULT now() NOT NULL, + modified timestamp without time zone +); + + +ALTER TABLE model.annotation_image OWNER TO openatlas; + +-- +-- Name: annotation_image_id_seq; Type: SEQUENCE; Schema: model; Owner: openatlas +-- + +CREATE SEQUENCE model.annotation_image_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE model.annotation_image_id_seq OWNER TO openatlas; + +-- +-- Name: annotation_image_id_seq; Type: SEQUENCE OWNED BY; Schema: model; Owner: openatlas +-- + +ALTER SEQUENCE model.annotation_image_id_seq OWNED BY model.annotation_image.id; + + +-- +-- Name: annotation_text_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas +-- + +CREATE SEQUENCE model.annotation_text_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + MAXVALUE 2147483647 + CACHE 1; + + +ALTER TABLE model.annotation_text_id_seq OWNER TO openatlas; + +-- +-- Name: annotation_text; Type: TABLE; Schema: model; Owner: openatlas +-- + +CREATE TABLE model.annotation_text ( + id integer DEFAULT nextval('model.annotation_text_id_seq'::regclass) NOT NULL, + source_id integer NOT NULL, + entity_id integer, + link_start integer NOT NULL, + link_end integer NOT NULL, + user_id integer, + text text, + created timestamp without time zone DEFAULT now() NOT NULL, + modified timestamp without time zone +); + + +ALTER TABLE model.annotation_text OWNER TO openatlas; + -- -- Name: cidoc_class; Type: TABLE; Schema: model; Owner: openatlas -- @@ -738,6 +811,7 @@ COMMENT ON COLUMN model.openatlas_class.layout_icon IS 'For Bootstrap icons'; -- CREATE SEQUENCE model.openatlas_class_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -862,78 +936,6 @@ ALTER TABLE model.property_inheritance_id_seq OWNER TO openatlas; ALTER SEQUENCE model.property_inheritance_id_seq OWNED BY model.property_inheritance.id; --- --- Name: annotation_image; Type: TABLE; Schema: web; Owner: openatlas --- - -CREATE TABLE web.annotation_image ( - id integer NOT NULL, - image_id integer NOT NULL, - entity_id integer, - coordinates text NOT NULL, - user_id integer, - text text, - created timestamp without time zone DEFAULT now() NOT NULL, - modified timestamp without time zone -); - - -ALTER TABLE web.annotation_image OWNER TO openatlas; - --- --- Name: annotation_image_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas --- - -CREATE SEQUENCE web.annotation_image_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE web.annotation_image_id_seq OWNER TO openatlas; - --- --- Name: annotation_image_id_seq; Type: SEQUENCE OWNED BY; Schema: web; Owner: openatlas --- - -ALTER SEQUENCE web.annotation_image_id_seq OWNED BY web.annotation_image.id; - - --- --- Name: annotation_text_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas --- - -CREATE SEQUENCE web.annotation_text_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - MAXVALUE 2147483647 - CACHE 1; - - -ALTER TABLE web.annotation_text_id_seq OWNER TO openatlas; - --- --- Name: annotation_text; Type: TABLE; Schema: web; Owner: openatlas --- - -CREATE TABLE web.annotation_text ( - id integer DEFAULT nextval('web.annotation_text_id_seq'::regclass) NOT NULL, - source_id integer NOT NULL, - entity_id integer, - link_start integer NOT NULL, - link_end integer NOT NULL, - user_id integer, - text text, - created timestamp without time zone DEFAULT now() NOT NULL, - modified timestamp without time zone -); - - -ALTER TABLE web.annotation_text OWNER TO openatlas; - -- -- Name: entity_profile_image; Type: TABLE; Schema: web; Owner: openatlas -- @@ -1016,7 +1018,8 @@ CREATE TABLE web.hierarchy ( created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone, category text DEFAULT 'standard'::text NOT NULL, - required boolean DEFAULT false NOT NULL + required boolean DEFAULT false NOT NULL, + allow_top_level_selection boolean DEFAULT true ); @@ -1247,6 +1250,7 @@ ALTER TABLE web.reference_system_openatlas_class OWNER TO openatlas; -- CREATE SEQUENCE web.reference_system_form_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -1297,26 +1301,12 @@ ALTER TABLE web.settings_id_seq OWNER TO openatlas; ALTER SEQUENCE web.settings_id_seq OWNED BY web.settings.id; --- --- Name: type_none_selectable_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas --- - -CREATE SEQUENCE web.type_none_selectable_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - MAXVALUE 2147483647 - CACHE 1; - - -ALTER TABLE web.type_none_selectable_id_seq OWNER TO openatlas; - -- -- Name: type_none_selectable; Type: TABLE; Schema: web; Owner: openatlas -- CREATE TABLE web.type_none_selectable ( - id integer DEFAULT nextval('web.type_none_selectable_id_seq'::regclass) NOT NULL, + id integer NOT NULL, entity_id integer NOT NULL, created timestamp without time zone DEFAULT now() NOT NULL ); @@ -1328,7 +1318,29 @@ ALTER TABLE web.type_none_selectable OWNER TO openatlas; -- Name: TABLE type_none_selectable; Type: COMMENT; Schema: web; Owner: openatlas -- -COMMENT ON TABLE web.type_none_selectable IS 'IDs of types that are not meant to be selected, e.g. a category'; +COMMENT ON TABLE web.type_none_selectable IS 'IDs of types that are not for selection, e.g. a category'; + + +-- +-- Name: type_none_selectable_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas +-- + +CREATE SEQUENCE web.type_none_selectable_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE web.type_none_selectable_id_seq OWNER TO openatlas; + +-- +-- Name: type_none_selectable_id_seq; Type: SEQUENCE OWNED BY; Schema: web; Owner: openatlas +-- + +ALTER SEQUENCE web.type_none_selectable_id_seq OWNED BY web.type_none_selectable.id; -- @@ -1539,6 +1551,13 @@ ALTER TABLE ONLY import.entity ALTER COLUMN id SET DEFAULT nextval('import.entit ALTER TABLE ONLY import.project ALTER COLUMN id SET DEFAULT nextval('import.project_id_seq'::regclass); +-- +-- Name: annotation_image id; Type: DEFAULT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_image ALTER COLUMN id SET DEFAULT nextval('model.annotation_image_id_seq'::regclass); + + -- -- Name: cidoc_class id; Type: DEFAULT; Schema: model; Owner: openatlas -- @@ -1609,13 +1628,6 @@ ALTER TABLE ONLY model.property_i18n ALTER COLUMN id SET DEFAULT nextval('model. ALTER TABLE ONLY model.property_inheritance ALTER COLUMN id SET DEFAULT nextval('model.property_inheritance_id_seq'::regclass); --- --- Name: annotation_image id; Type: DEFAULT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_image ALTER COLUMN id SET DEFAULT nextval('web.annotation_image_id_seq'::regclass); - - -- -- Name: entity_profile_image id; Type: DEFAULT; Schema: web; Owner: openatlas -- @@ -1679,6 +1691,13 @@ ALTER TABLE ONLY web.settings ALTER COLUMN id SET DEFAULT nextval('web.settings_ ALTER TABLE ONLY web.system_log ALTER COLUMN id SET DEFAULT nextval('web.log_id_seq'::regclass); +-- +-- Name: type_none_selectable id; Type: DEFAULT; Schema: web; Owner: openatlas +-- + +ALTER TABLE ONLY web.type_none_selectable ALTER COLUMN id SET DEFAULT nextval('web.type_none_selectable_id_seq'::regclass); + + -- -- Name: user id; Type: DEFAULT; Schema: web; Owner: openatlas -- @@ -1746,6 +1765,22 @@ ALTER TABLE ONLY import.project ADD CONSTRAINT project_pkey PRIMARY KEY (id); +-- +-- Name: annotation_image annotation_image_pkey; Type: CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_image + ADD CONSTRAINT annotation_image_pkey PRIMARY KEY (id); + + +-- +-- Name: annotation_text annotation_text_pkey; Type: CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_text + ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); + + -- -- Name: cidoc_class class_code_key; Type: CONSTRAINT; Schema: model; Owner: openatlas -- @@ -1898,22 +1933,6 @@ ALTER TABLE ONLY model.property ADD CONSTRAINT property_pkey PRIMARY KEY (id); --- --- Name: annotation_image annotation_image_pkey; Type: CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_image - ADD CONSTRAINT annotation_image_pkey PRIMARY KEY (id); - - --- --- Name: annotation_text annotation_text_pkey; Type: CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_text - ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); - - -- -- Name: type_none_selectable entity_id_key; Type: CONSTRAINT; Schema: web; Owner: openatlas -- @@ -2177,45 +2196,45 @@ CREATE TRIGGER on_delete_entity BEFORE DELETE ON model.entity FOR EACH ROW EXECU -- --- Name: entity update_modified; Type: TRIGGER; Schema: model; Owner: openatlas +-- Name: annotation_image update_modified; Type: TRIGGER; Schema: model; Owner: openatlas -- -CREATE TRIGGER update_modified BEFORE UPDATE ON model.entity FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- --- Name: file_info update_modified; Type: TRIGGER; Schema: model; Owner: openatlas +-- Name: annotation_text update_modified; Type: TRIGGER; Schema: model; Owner: openatlas -- -CREATE TRIGGER update_modified BEFORE UPDATE ON model.file_info FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- --- Name: gis update_modified; Type: TRIGGER; Schema: model; Owner: openatlas +-- Name: entity update_modified; Type: TRIGGER; Schema: model; Owner: openatlas -- -CREATE TRIGGER update_modified BEFORE UPDATE ON model.gis FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.entity FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- --- Name: link update_modified; Type: TRIGGER; Schema: model; Owner: openatlas +-- Name: file_info update_modified; Type: TRIGGER; Schema: model; Owner: openatlas -- -CREATE TRIGGER update_modified BEFORE UPDATE ON model.link FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.file_info FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- --- Name: annotation_image update_modified; Type: TRIGGER; Schema: web; Owner: openatlas +-- Name: gis update_modified; Type: TRIGGER; Schema: model; Owner: openatlas -- -CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.gis FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- --- Name: annotation_text update_modified; Type: TRIGGER; Schema: web; Owner: openatlas +-- Name: link update_modified; Type: TRIGGER; Schema: model; Owner: openatlas -- -CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.link FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- @@ -2312,6 +2331,54 @@ ALTER TABLE ONLY import.entity ADD CONSTRAINT entity_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; +-- +-- Name: annotation_image annotation_image_entity_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_image + ADD CONSTRAINT annotation_image_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE SET NULL; + + +-- +-- Name: annotation_image annotation_image_image_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_image + ADD CONSTRAINT annotation_image_image_id_fkey FOREIGN KEY (image_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: annotation_image annotation_image_user_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_image + ADD CONSTRAINT annotation_image_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE SET NULL; + + +-- +-- Name: annotation_text annotation_text_entity_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_text + ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE SET NULL; + + +-- +-- Name: annotation_text annotation_text_source_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_text + ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: annotation_text annotation_text_user_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas +-- + +ALTER TABLE ONLY model.annotation_text + ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; + + -- -- Name: cidoc_class_i18n class_i18n_class_code_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas -- @@ -2464,54 +2531,6 @@ ALTER TABLE ONLY model.property ADD CONSTRAINT property_range_class_code_fkey FOREIGN KEY (range_class_code) REFERENCES model.cidoc_class(code) ON UPDATE CASCADE ON DELETE CASCADE; --- --- Name: annotation_image annotation_image_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_image - ADD CONSTRAINT annotation_image_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE SET NULL; - - --- --- Name: annotation_image annotation_image_image_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_image - ADD CONSTRAINT annotation_image_image_id_fkey FOREIGN KEY (image_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- Name: annotation_image annotation_image_user_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_image - ADD CONSTRAINT annotation_image_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE SET NULL; - - --- --- Name: annotation_text annotation_text_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_text - ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- Name: annotation_text annotation_text_source_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_text - ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- Name: annotation_text annotation_text_user_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas --- - -ALTER TABLE ONLY web.annotation_text - ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; - - -- -- Name: entity_profile_image entity_profile_image_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas -- diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql index 012f56e0d..c0f07cbb1 100644 --- a/install/upgrade/text_annotation.sql +++ b/install/upgrade/text_annotation.sql @@ -3,25 +3,26 @@ BEGIN; -- #2079: Text annotation -- Sync image annotation fields to text annotation fields -ALTER TABLE web.annotation_image RENAME COLUMN annotation TO text; -ALTER TABLE web.annotation_image ALTER COLUMN text DROP NOT NULL; -ALTER TABLE web.annotation_image ADD COLUMN modified timestamp without time zone; -CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +ALTER TABLE web.annotation_image SET SCHEMA model; +ALTER TABLE model.annotation_image RENAME COLUMN annotation TO text; +ALTER TABLE model.annotation_image ALTER COLUMN text DROP NOT NULL; +ALTER TABLE model.annotation_image ADD COLUMN modified timestamp without time zone; +CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -- Text annotation -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; -DROP TRIGGER IF EXISTS update_modified ON web.annotation_text; -ALTER TABLE IF EXISTS ONLY web.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_pkey; -DROP TABLE IF EXISTS web.annotation_text; -DROP SEQUENCE IF EXISTS web.annotation_text_id_seq; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; +DROP TRIGGER IF EXISTS update_modified ON model.annotation_text; +ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_pkey; +DROP TABLE IF EXISTS model.annotation_text; +DROP SEQUENCE IF EXISTS model.annotation_text_id_seq; -CREATE SEQUENCE web.annotation_text_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE MAXVALUE 2147483647 CACHE 1; -ALTER TABLE web.annotation_text_id_seq OWNER TO openatlas; +CREATE SEQUENCE model.annotation_text_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE MAXVALUE 2147483647 CACHE 1; +ALTER TABLE model.annotation_text_id_seq OWNER TO openatlas; -CREATE TABLE web.annotation_text ( - id integer DEFAULT nextval('web.annotation_text_id_seq'::regclass) NOT NULL, +CREATE TABLE model.annotation_text ( + id integer DEFAULT nextval('model.annotation_text_id_seq'::regclass) NOT NULL, source_id integer NOT NULL, entity_id integer, link_start integer NOT NULL, @@ -31,12 +32,12 @@ CREATE TABLE web.annotation_text ( created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone ); -ALTER TABLE web.annotation_text OWNER TO openatlas; +ALTER TABLE model.annotation_text OWNER TO openatlas; -ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); -CREATE TRIGGER update_modified BEFORE UPDATE ON web.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; -ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; -ALTER TABLE ONLY web.annotation_text ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); +CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); +ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; END; diff --git a/openatlas/database/annotation.py b/openatlas/database/annotation.py index b4be486ce..b2459460a 100644 --- a/openatlas/database/annotation.py +++ b/openatlas/database/annotation.py @@ -12,7 +12,7 @@ user_id, text, created - FROM web.annotation_image + FROM model.annotation_image """ ANNOTATION_TEXT_SELECT = \ @@ -26,7 +26,7 @@ user_id, text, created - FROM web.annotation_text + FROM model.annotation_text """ @@ -73,7 +73,7 @@ def get_annotation_image_orphans() -> list[dict[str, Any]]: a.user_id, a.text, a.created - FROM web.annotation_image a + FROM model.annotation_image a LEFT JOIN model.link l ON l.domain_id = a.image_id AND l.range_id = a.entity_id AND l.property_code = 'P67' @@ -85,7 +85,7 @@ def get_annotation_image_orphans() -> list[dict[str, Any]]: def insert_annotation_image(data: dict[str, Any]) -> None: g.cursor.execute( """ - INSERT INTO web.annotation_image ( + INSERT INTO model.annotation_image ( image_id, entity_id, coordinates, @@ -104,7 +104,7 @@ def insert_annotation_image(data: dict[str, Any]) -> None: def update_annotation_image(data: dict[str, Any]) -> None: g.cursor.execute( """ - UPDATE web.annotation_image + UPDATE model.annotation_image SET (entity_id, text) = (%(entity_id)s, %(text)s) WHERE id = %(id)s; """, @@ -113,7 +113,7 @@ def update_annotation_image(data: dict[str, Any]) -> None: def delete_annotation_image(id_: int) -> None: g.cursor.execute( - 'DELETE FROM web.annotation_image WHERE id = %(id)s;', + 'DELETE FROM model.annotation_image WHERE id = %(id)s;', {'id': id_}) @@ -121,7 +121,7 @@ def update_annotation_text(data: dict[str, Any]) -> None: print(data) g.cursor.execute( """ - UPDATE web.annotation_text + UPDATE model.annotation_text SET (entity_id, text, link_start, link_end) = (%(entity_id)s, %(text)s, %(link_start)s, %(link_end)s) WHERE id = %(id)s; @@ -131,7 +131,7 @@ def update_annotation_text(data: dict[str, Any]) -> None: def delete_annotation_text(id_: int) -> None: g.cursor.execute( - 'DELETE FROM web.annotation_text WHERE id = %(id)s;', + 'DELETE FROM model.annotation_text WHERE id = %(id)s;', {'id': id_}) @@ -140,7 +140,7 @@ def remove_entity_from_annotation_image( entity_id: int) -> None: g.cursor.execute( """ - UPDATE web.annotation_image + UPDATE model.annotation_image SET entity_id = NULL WHERE id = %(annotation_id)s AND entity_id = %(entity_id)s; """, @@ -150,7 +150,7 @@ def remove_entity_from_annotation_image( def insert_annotation_text(data: dict[str, Any]) -> None: g.cursor.execute( """ - INSERT INTO web.annotation_text ( + INSERT INTO model.annotation_text ( source_id, entity_id, link_start, From 29e725e613b753ff437f4b76fcf100e12f32e76f Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Mon, 4 Nov 2024 17:45:10 +0100 Subject: [PATCH 11/43] Removed user_ids from annoations --- install/1_structure.sql | 20 -------------------- install/upgrade/text_annotation.sql | 3 +-- openatlas/database/annotation.py | 7 ------- openatlas/models/annotation.py | 6 +----- 4 files changed, 2 insertions(+), 34 deletions(-) diff --git a/install/1_structure.sql b/install/1_structure.sql index f60de97ed..580ac5d41 100644 --- a/install/1_structure.sql +++ b/install/1_structure.sql @@ -51,10 +51,8 @@ ALTER TABLE IF EXISTS ONLY model.entity DROP CONSTRAINT IF EXISTS entity_class_c ALTER TABLE IF EXISTS ONLY model.cidoc_class_inheritance DROP CONSTRAINT IF EXISTS class_inheritance_super_code_fkey; ALTER TABLE IF EXISTS ONLY model.cidoc_class_inheritance DROP CONSTRAINT IF EXISTS class_inheritance_sub_code_fkey; ALTER TABLE IF EXISTS ONLY model.cidoc_class_i18n DROP CONSTRAINT IF EXISTS class_i18n_class_code_fkey; -ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_user_id_fkey; ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_source_id_fkey; ALTER TABLE IF EXISTS ONLY model.annotation_text DROP CONSTRAINT IF EXISTS annotation_text_entity_id_fkey; -ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_user_id_fkey; ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_image_id_fkey; ALTER TABLE IF EXISTS ONLY model.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_entity_id_fkey; ALTER TABLE IF EXISTS ONLY import.entity DROP CONSTRAINT IF EXISTS entity_user_id_fkey; @@ -413,7 +411,6 @@ CREATE TABLE model.annotation_image ( image_id integer NOT NULL, entity_id integer, coordinates text NOT NULL, - user_id integer, text text, created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone @@ -467,7 +464,6 @@ CREATE TABLE model.annotation_text ( entity_id integer, link_start integer NOT NULL, link_end integer NOT NULL, - user_id integer, text text, created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone @@ -2347,14 +2343,6 @@ ALTER TABLE ONLY model.annotation_image ADD CONSTRAINT annotation_image_image_id_fkey FOREIGN KEY (image_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; --- --- Name: annotation_image annotation_image_user_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas --- - -ALTER TABLE ONLY model.annotation_image - ADD CONSTRAINT annotation_image_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE SET NULL; - - -- -- Name: annotation_text annotation_text_entity_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas -- @@ -2371,14 +2359,6 @@ ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; --- --- Name: annotation_text annotation_text_user_id_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas --- - -ALTER TABLE ONLY model.annotation_text - ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; - - -- -- Name: cidoc_class_i18n class_i18n_class_code_fkey; Type: FK CONSTRAINT; Schema: model; Owner: openatlas -- diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql index c0f07cbb1..15e2dda46 100644 --- a/install/upgrade/text_annotation.sql +++ b/install/upgrade/text_annotation.sql @@ -6,6 +6,7 @@ BEGIN; ALTER TABLE web.annotation_image SET SCHEMA model; ALTER TABLE model.annotation_image RENAME COLUMN annotation TO text; ALTER TABLE model.annotation_image ALTER COLUMN text DROP NOT NULL; +ALTER TABLE model.annotation_image DROP COLUMN user_id; ALTER TABLE model.annotation_image ADD COLUMN modified timestamp without time zone; CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_image FOR EACH ROW EXECUTE FUNCTION model.update_modified(); @@ -27,7 +28,6 @@ CREATE TABLE model.annotation_text ( entity_id integer, link_start integer NOT NULL, link_end integer NOT NULL, - user_id integer, text text, created timestamp without time zone DEFAULT now() NOT NULL, modified timestamp without time zone @@ -38,6 +38,5 @@ ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_pkey PRIMA CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; -ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE CASCADE; END; diff --git a/openatlas/database/annotation.py b/openatlas/database/annotation.py index b2459460a..1622eee22 100644 --- a/openatlas/database/annotation.py +++ b/openatlas/database/annotation.py @@ -9,7 +9,6 @@ image_id, entity_id, coordinates, - user_id, text, created FROM model.annotation_image @@ -23,7 +22,6 @@ entity_id, link_start, link_end, - user_id, text, created FROM model.annotation_text @@ -70,7 +68,6 @@ def get_annotation_image_orphans() -> list[dict[str, Any]]: a.image_id, a.entity_id, a.coordinates, - a.user_id, a.text, a.created FROM model.annotation_image a @@ -89,13 +86,11 @@ def insert_annotation_image(data: dict[str, Any]) -> None: image_id, entity_id, coordinates, - user_id, text ) VALUES ( %(image_id)s, %(entity_id)s, %(coordinates)s, - %(user_id)s, %(text)s); """, data) @@ -155,14 +150,12 @@ def insert_annotation_text(data: dict[str, Any]) -> None: entity_id, link_start, link_end, - user_id, text ) VALUES ( %(source_id)s, %(entity_id)s, %(link_start)s, %(link_end)s, - %(user_id)s, %(text)s); """, data) diff --git a/openatlas/models/annotation.py b/openatlas/models/annotation.py index 58b0f688e..8c672aadc 100644 --- a/openatlas/models/annotation.py +++ b/openatlas/models/annotation.py @@ -13,7 +13,6 @@ def __init__(self, data: dict[str, Any]) -> None: self.image_id = data['image_id'] self.entity_id = data['entity_id'] self.coordinates = data['coordinates'] - self.user_id = data['user_id'] self.text = data['text'] self.created = data['created'] @@ -58,7 +57,6 @@ def insert( text: Optional[str] = None) -> None: db.insert_annotation_image({ 'image_id': image_id, - 'user_id': current_user.id, 'entity_id': entity_id or None, 'coordinates': coordinates, 'text': text}) @@ -71,7 +69,6 @@ def __init__(self, data: dict[str, Any]) -> None: self.entity_id = data['entity_id'] self.link_start = data['link_start'] self.link_end = data['link_end'] - self.user_id = data['user_id'] self.text = data['text'] self.created = data['created'] @@ -113,5 +110,4 @@ def insert( 'link_start': link_start, 'link_end': link_end, 'entity_id': entity_id, - 'text': text, - 'user_id': current_user.id}) + 'text': text}) From 43f640c2527475a21bef067b4c6dd00622730c8f Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Wed, 6 Nov 2024 14:48:54 +0100 Subject: [PATCH 12/43] Fix for annotation SQL update --- install/upgrade/text_annotation.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/upgrade/text_annotation.sql b/install/upgrade/text_annotation.sql index 15e2dda46..e921f76a4 100644 --- a/install/upgrade/text_annotation.sql +++ b/install/upgrade/text_annotation.sql @@ -36,7 +36,7 @@ ALTER TABLE model.annotation_text OWNER TO openatlas; ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_pkey PRIMARY KEY (id); CREATE TRIGGER update_modified BEFORE UPDATE ON model.annotation_text FOR EACH ROW EXECUTE FUNCTION model.update_modified(); -ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; +ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE SET NULL; ALTER TABLE ONLY model.annotation_text ADD CONSTRAINT annotation_text_source_id_fkey FOREIGN KEY (source_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE; END; From a361de6ae4ba56cd8733f5cf0b6f38362989d571 Mon Sep 17 00:00:00 2001 From: Alexander Watzinger Date: Mon, 9 Dec 2024 15:21:48 +0100 Subject: [PATCH 13/43] Fixed tab navigation --- openatlas/templates/tabs.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openatlas/templates/tabs.html b/openatlas/templates/tabs.html index 337f01e3f..4de4562e6 100644 --- a/openatlas/templates/tabs.html +++ b/openatlas/templates/tabs.html @@ -7,7 +7,7 @@