Пример #1
0
 def update(self, session: Session, title: str, visible: bool, status: str, projtype: str,
            init_classif_list: List[int],
            classiffieldlist: str, popoverfieldlist: str,
            cnn_network_id: str, comments: str,
            contact: Any,
            managers: List[Any], annotators: List[Any], viewers: List[Any],
            license_: str):
     assert contact is not None, "A valid Contact is needed."
     proj_id = self._project.projid
     # Field reflexes
     if cnn_network_id != self._project.cnn_network_id:
         sub_qry: Query = session.query(ObjectHeader.objid)
         sub_qry = sub_qry.join(Acquisition, Acquisition.acquisid == ObjectHeader.acquisid)
         sub_qry = sub_qry.join(Sample, and_(Sample.sampleid == Acquisition.acq_sample_id,
                                             Sample.projid == proj_id))
         # Delete CNN features which depend on the CNN network
         qry: Query = session.query(ObjectCNNFeature)
         qry = qry.filter(ObjectCNNFeature.objcnnid.in_(sub_qry.subquery()))
         qry.delete(synchronize_session=False)
     # Fields update
     self._project.title = title
     self._project.visible = visible
     self._project.status = status
     self._project.projtype = projtype
     self._project.classiffieldlist = classiffieldlist
     self._project.popoverfieldlist = popoverfieldlist
     self._project.cnn_network_id = cnn_network_id
     self._project.comments = comments
     self._project.license = license_
     # Inverse for extracted values
     self._project.initclassiflist = ",".join([str(cl_id) for cl_id in init_classif_list])
     # Inverse for users by privilege
     # Dispatch members by right
     # TODO: Nothing prevents or cares about redundant rights, such as adding same
     #     user as both Viewer and Annotator.
     by_right = {ProjectPrivilegeBO.MANAGE: managers,
                 ProjectPrivilegeBO.ANNOTATE: annotators,
                 ProjectPrivilegeBO.VIEW: viewers}
     # Remove all to avoid tricky diffs
     session.query(ProjectPrivilege). \
         filter(ProjectPrivilege.projid == proj_id).delete()
     # Add all
     contact_used = False
     for a_right, a_user_list in by_right.items():
         for a_user in a_user_list:
             # Set flag for contact person
             extra = None
             if a_user.id == contact.id and a_right == ProjectPrivilegeBO.MANAGE:
                 extra = 'C'
                 contact_used = True
             session.add(ProjectPrivilege(projid=proj_id,
                                          member=a_user.id,
                                          privilege=a_right,
                                          extra=extra))
     # Sanity check
     assert contact_used, "Could not set Contact, the designated user is not in Managers list."
     session.commit()
Пример #2
0
 def delete(session: Session, prj_id: int):
     """
         Completely remove the project. It is assumed that contained objects have been removed.
     """
     # TODO: Remove from user preferences
     # Unlink Particle project if any
     upd_qry = ParticleProject.__table__.update().where(ParticleProject.projid == prj_id).values(projid=None)
     row_count = session.execute(upd_qry).rowcount
     logger.info("%d EcoPart project unlinked", row_count)
     # Remove project
     session.query(Project). \
         filter(Project.projid == prj_id).delete()
     # Remove privileges
     session.query(ProjectPrivilege). \
         filter(ProjectPrivilege.projid == prj_id).delete()
Пример #3
0
 def __init__(self, session: Session, prj_ids: ProjectIDListT, public: bool = False):
     # Query the project and load neighbours as well
     qry: Query = session.query(Project, ProjectPrivilege)
     qry = qry.outerjoin(ProjectPrivilege, Project.privs_for_members).options(
         contains_eager(Project.privs_for_members))
     qry = qry.outerjoin(User, ProjectPrivilege.user).options(
         contains_eager(ProjectPrivilege.user))
     qry = qry.filter(Project.projid == any_(prj_ids))
     self.projects = []
     done = set()
     with CodeTimer("%s BO projects query & init:" % len(prj_ids), logger):
         for a_proj, a_pp in qry.all():
             # The query yields duplicates so we need to filter
             if a_proj.projid not in done:
                 if public:
                     self.projects.append(ProjectBO(a_proj))
                 else:
                     self.projects.append(ProjectBO(a_proj).enrich())
                 done.add(a_proj.projid)
     # Add instruments
     with CodeTimer("%s set instruments:" % len(prj_ids), logger):
         instruments = DescribedInstrumentSet(session, prj_ids)
         for a_project in self.projects:
             instrums = instruments.by_project.get(a_project.projid)
             if instrums is not None:
                 a_project.instrument = ",".join(instrums)
Пример #4
0
    def remap(session: Session, prj_id: int, table: MappedTableTypeT, remaps: List[RemapOp]):
        """
            Apply remapping operations onto the given table for given project.
        """
        # Do the remapping, including blanking of unused columns
        values = {a_remap.to: text(a_remap.frm) if a_remap.frm is not None else a_remap.frm
                  for a_remap in remaps}
        qry: Query = session.query(table)
        samples_4_prj: Query
        acqs_4_samples: Query
        if table == Sample:
            qry = qry.filter(Sample.projid == prj_id)  # type: ignore
        elif table == Acquisition:
            samples_4_prj = Query(Sample.sampleid).filter(Sample.projid == prj_id)
            qry = qry.filter(Acquisition.acq_sample_id.in_(samples_4_prj))  # type: ignore
        elif table == Process:
            samples_4_prj = Query(Sample.sampleid).filter(Sample.projid == prj_id)
            acqs_4_samples = Query(Acquisition.acquisid).filter(Acquisition.acq_sample_id.in_(samples_4_prj))
            qry = qry.filter(Process.processid.in_(acqs_4_samples))  # type: ignore
        elif table == ObjectFields:
            samples_4_prj = Query(Sample.sampleid).filter(Sample.projid == prj_id)
            acqs_4_samples = Query(Acquisition.acquisid).filter(Acquisition.acq_sample_id.in_(samples_4_prj))
            objs_for_acqs: Query = Query(ObjectHeader.objid).filter(ObjectHeader.acquisid.in_(acqs_4_samples))
            qry = qry.filter(ObjectFields.objfid.in_(objs_for_acqs))  # type: ignore
        qry = qry.update(values=values, synchronize_session=False)

        logger.info("Remap query for %s: %s", table.__tablename__, qry)
Пример #5
0
 def get_all_object_ids(cls, session: Session, prj_id: int):  # TODO: Problem with recursive import -> ObjetIdListT:
     """
         Return the full list of objects IDs inside a project.
         TODO: Maybe better in ObjectBO
     """
     qry: Query = session.query(ObjectHeader.objid)
     qry = qry.join(Acquisition, Acquisition.acquisid == ObjectHeader.acquisid)
     qry = qry.join(Sample, and_(Sample.sampleid == Acquisition.acq_sample_id,
                                 Sample.projid == prj_id))
     return [an_id for an_id, in qry.all()]
Пример #6
0
 def create_or_link_slaves(how: ImportHow, session: Session,
                           object_head_to_write, object_fields_to_write,
                           image_to_write) -> int:
     """
         Create, link or update slave entities, i.e. head, fields, image.
         Also update them... TODO: Split/fork the def
         :returns the number of new records
     """
     if object_head_to_write.orig_id in how.existing_objects:
         # Set the objid which will be copied for storing the image, the object itself
         # will not be stored due to returned value.
         objid = how.existing_objects[object_head_to_write.orig_id]
         object_head_to_write.objid = objid
         if how.can_update_only:
             # noinspection DuplicatedCode
             for a_cls, its_pk, an_upd in zip(
                 [ObjectHeader, ObjectFields], ['objid', 'objfid'],
                 [object_head_to_write, object_fields_to_write]):
                 filter_for_id = text("%s=%d" % (its_pk, objid))
                 # Fetch the record to update
                 obj = session.query(a_cls).filter(filter_for_id).first()
                 if a_cls == ObjectHeader:
                     # Eventually refresh sun position
                     if an_upd.nb_fields_from(USED_FIELDS_FOR_SUNPOS) > 0:
                         # Give the bean enough data for computation
                         for a_field in USED_FIELDS_FOR_SUNPOS.difference(
                                 an_upd.keys()):
                             an_upd[a_field] = getattr(obj, a_field)
                         TSVFile.do_sun_position_field(an_upd)
                 updates = TSVFile.update_orm_object(obj,
                                                     an_upd)  # type: ignore
                 if len(updates) > 0:
                     logger.info("Updating '%s' using %s", filter_for_id,
                                 updates)
                     session.flush()
             ret = 0  # nothing to write
         else:
             # 'Simply' a line with a complementary image
             logger.info("One more image for %s:%s ",
                         object_head_to_write.orig_id, image_to_write)
             ret = 1  # just a new image
     else:
         if how.can_update_only:
             # No objects creation while updating
             logger.info("Object %s not found while updating ",
                         object_head_to_write.orig_id)
             ret = 0
         else:
             # or create it
             # object_head_to_write.projid = how.prj_id
             object_head_to_write.random_value = random.randint(1, 99999999)
             # Below left NULL @see self.update_counts_and_img0
             # object_head_to_write.img0id = XXXXX
             ret = 3  # new image + new object_head + new object_fields
     return ret
Пример #7
0
 def list_public_projects(session: Session,
                          title_filter: str = '') -> List[ProjectIDT]:
     """
     :param session:
     :param title_filter: If set, filter out the projects with title not matching the required string.
     :return: The project IDs
     """
     pattern = '%' + title_filter + '%'
     qry: Query = session.query(Project.projid)
     qry = qry.filter(Project.visible)
     qry = qry.filter(Project.title.ilike(pattern))
     ret = [an_id for an_id, in qry.all()]
     return ret
Пример #8
0
 def get_all_num_columns_values(self, session: Session):
     """
         Get all numerical free fields values for all objects in a project.
     """
     from DB.helpers.ORM import MetaData
     metadata = MetaData(bind=session.get_bind())
     # TODO: Cache in a member
     mappings = ProjectMapping().load_from_project(self._project)
     num_fields_cols = set([col for col in mappings.object_mappings.tsv_cols_to_real.values()
                            if col[0] == 'n'])
     obj_fields_tbl = minimal_table_of(metadata, ObjectFields, num_fields_cols, exact_floats=True)
     qry: Query = session.query(Project)
     qry = qry.join(Project.all_samples).join(Sample.all_acquisitions).join(Acquisition.all_objects)
     qry = qry.join(obj_fields_tbl, ObjectHeader.objid == obj_fields_tbl.c.objfid)
     qry = qry.filter(Project.projid == self._project.projid)
     qry = qry.order_by(Acquisition.acquisid)
     qry = qry.with_entities(Acquisition.acquisid, Acquisition.orig_id, obj_fields_tbl)
     return qry.all()
Пример #9
0
    def update_parent_objects(how: ImportHow, session: Session,
                              dicts_for_update: Mapping[str, Dict]):
        """
            Update of Sample, Acquisition & Process.
            For locating the records, we tolerate the lack of orig_id like during creation.
        """
        assert how.can_update_only
        upper_level_pk = how.prj_id
        # Loop up->down, i.e. Sample to Process
        parent_class: ParentTableClassT
        for alias, parent_class in GlobalMapping.PARENT_CLASSES.items():
            # The data from TSV, to update with. Eventually just an empty dict, but still a dict.
            dict_for_update = dicts_for_update[alias]

            if parent_class != Process:
                # Locate using Sample & Acquisition orig_id
                parent_orig_id = dict_for_update.get("orig_id")
                if parent_orig_id is None:
                    # No orig_id for parent object in provided dict
                    # Default with present parent's parent technical ID
                    parent_orig_id = '__DUMMY_ID__%d__' % upper_level_pk
                # Look for the parent by its (eventually amended) orig_id
                parents_for_obj: Dict[
                    str, ParentTableT] = how.existing_parents[alias]
                parent = parents_for_obj.get(parent_orig_id)
                if parent is None:
                    # No parent found to update, thus we cannot locate children, as there
                    # is an implicit relationship just by the fact that the 3 are on the same line
                    break
                # Collect the PK for children in case we need to use a __DUMMY
                upper_level_pk = parent.pk()
            else:
                # Fetch the process from DB
                parent = session.query(Process).get(upper_level_pk)
                assert parent is not None

            # OK we have something to update
            # Update the DB line using sqlalchemy
            updates = TSVFile.update_orm_object(parent, dict_for_update)
            if len(updates) > 0:
                logger.info("Updating %s '%s' using %s", alias, parent.orig_id,
                            updates)
                session.flush()
Пример #10
0
 def read_user_stats(session: Session, prj_ids: ProjectIDListT) -> List[ProjectUserStats]:
     """
         Read the users (annotators) involved in each project.
         Also compute a summary of their activity. This can only be an estimate since, e.g.
         imported data contains exact same data as the one obtained from live actions.
     """
     # Activity count: Count 1 for present classification for a user per object.
     #  Of course, the classification date is the latest for the user.
     pqry: Query = session.query(Project.projid, User.id, User.name,
                                 func.count(ObjectHeader.objid),
                                 func.max(ObjectHeader.classif_when))
     pqry = pqry.join(Sample).join(Acquisition).join(ObjectHeader)
     pqry = pqry.join(User, User.id == ObjectHeader.classif_who)
     pqry = pqry.filter(Project.projid == any_(prj_ids))
     pqry = pqry.filter(ObjectHeader.classif_who == User.id)
     pqry = pqry.group_by(Project.projid, User.id)
     pqry = pqry.order_by(Project.projid, User.name)
     ret = []
     user_activities: Dict[UserIDT, UserActivity] = {}
     user_activities_per_project = {}
     stats_per_project = {}
     with CodeTimer("user present stats for %d projects, qry: %s:" % (len(prj_ids), str(pqry)), logger):
         last_prj = None
         for projid, user_id, user_name, cnt, last_date in pqry.all():
             last_date_str = last_date.replace(microsecond=0).isoformat()
             if projid != last_prj:
                 last_prj = projid
                 prj_stat = ProjectUserStats((projid, [], []))
                 ret.append(prj_stat)
                 user_activities = {}
                 # Store for second pass with history
                 stats_per_project[projid] = prj_stat
                 user_activities_per_project[projid] = user_activities
             prj_stat.annotators.append(MinimalUserBO((user_id, user_name)))
             user_activity = UserActivity((user_id, cnt, last_date_str))
             prj_stat.activities.append(user_activity)
             # Store for second pass
             user_activities[user_id] = user_activity
     # Activity count update: Add 1 for each entry in history for each user.
     # The dates in history are ignored, except for users which do not appear in first resultset.
     hqry: Query = session.query(Project.projid, User.id, User.name,
                                 func.count(ObjectsClassifHisto.objid),
                                 func.max(ObjectsClassifHisto.classif_date))
     hqry = hqry.join(Sample).join(Acquisition).join(ObjectHeader).join(ObjectsClassifHisto)
     hqry = hqry.join(User, User.id == ObjectsClassifHisto.classif_who)
     hqry = hqry.filter(Project.projid == any_(prj_ids))
     hqry = hqry.group_by(Project.projid, User.id)
     hqry = hqry.order_by(Project.projid, User.name)
     with CodeTimer("user history stats for %d projects, qry: %s:" % (len(prj_ids), str(hqry)), logger):
         last_prj = None
         for projid, user_id, user_name, cnt, last_date in hqry.all():
             last_date_str = last_date.replace(microsecond=0).isoformat()
             if projid != last_prj:
                 last_prj = projid
                 # Just in case
                 if projid not in user_activities_per_project:
                     continue
                 # Get stored data for the project
                 user_activities = user_activities_per_project[projid]
                 prj_stat = stats_per_project[projid]
             already_there = user_activities.get(user_id)
             if already_there is not None:
                 # A user in both history and present classification
                 already_there.nb_actions += cnt
             else:
                 # A user _only_ in history
                 prj_stat.annotators.append(MinimalUserBO((user_id, user_name)))
                 user_activity = UserActivity((user_id, cnt, last_date_str))
                 prj_stat.activities.append(user_activity)
                 user_activities[user_id] = user_activity
     return ret