<commit_before>package be.cytomine.apps.iris

import grails.converters.JSON

import org.codehaus.groovy.runtime.typehandling.GroovyCastException;
import org.json.simple.JSONArray
import org.json.simple.JSONObject

import be.cytomine.client.Cytomine
import be.cytomine.client.CytomineException;
import be.cytomine.client.collections.AnnotationCollection;
import be.cytomine.client.collections.TermCollection;
import be.cytomine.client.models.Annotation;
import be.cytomine.client.models.AnnotationTerm;
import be.cytomine.client.models.ImageInstance;
import be.cytomine.client.models.Ontology
import be.cytomine.clientx.CytomineX;


/**
 * 
 * @author <NAME>
 *
 */
class AnnotationController {

	def grailsApplication
	def sessionService
	def annotationService

	def beforeInterceptor = {
		log.debug("Executing action $actionName with params $params")
	}

	/**
	 * Get all annotations for a specific project and image.
	 * 
	 * @return the annotations as JSON array
	 */
	def getAnnotations(){
		try {
			Cytomine cytomine = request['cytomine']
			String publicKey = params['publicKey']

			long sessionID = params.long('sessionID')
			long projectID = params.long('cmProjectID')

			// filter for a specific project/image/term
			Map<String,String> filters = new HashMap<String,String>()
			filters.put("project", String.valueOf(projectID))
			filters.put("showGIS", "true") // show the centroid information on the location
			filters.put("showMeta", "true") // show the meta informations
			filters.put("showTerm", "true") // show the term informations

			String imageIDs = params['images']
			if (imageIDs != null){
				filters.put("images", String.valueOf(imageIDs))
			}

			// filter for terms
			String termIDs = params['terms']
			if (termIDs != null){
				filters.put("terms", String.valueOf(termIDs))
			}
			// additionally check for termID 0, this will be annotations without terms
			if (params['noTerm'] != null && params['noTerm'].equals("true")){
				filters.put("noTerm", "true")
			}
			// check for multiple terms
			if (params['multipleTerm'] != null && params['multipleTerm'].equals("true")){
				filters.put("multipleTerm", "true")
			}

			// TODO implement pagination
			int max = (params['max']==null?0:params.int('max'))

			// get the session and the user
			Session sess = Session.get(sessionID)
			User user = sess.getUser()

			// find the project
			Project p = sess.getProjects().find { it.cmID == projectID }
			long ontologyID = p.getCmOntology();

			// fetch the terms from the ontology
			TermCollection terms = cytomine.getTermsByOntology(ontologyID)

			// get all annotations according to the filter
			AnnotationCollection annotations = cytomine.getAnnotations(filters)
			def irisAnnList = new JSONArray()

			// create a new domain mapper
			DomainMapper dm = new DomainMapper(grailsApplication)

			for(int i=0;i<annotations.size();i++) {
				Annotation annotation = annotations.get(i)

				// map the annotation to the IRIS model
				be.cytomine.apps.iris.Annotation irisAnn = dm.mapAnnotation(annotation, null)

				irisAnn = annotationService.resolveTerms(ontologyID, terms, user, annotation, irisAnn)

				// ######################################################################################
				// IMPORTANT: do NOT inject the cytomine annotation, because they contain information on
				// mappings done by other users
				// ######################################################################################
				// add the annotation to the result and use the AnnotationMarshaller in order to
				// serialize the domain objects
				irisAnnList.add(irisAnn)
			}

			render (irisAnnList as JSON)

		} catch(CytomineException e1){
			// exceptions from the cytomine java client
			response.setStatus(e1.httpCode)
			JSONObject errorMsg = new Utils().resolveCytomineException(e1)
			render errorMsg as JSON
		} catch(GroovyCastException e2) {
			// send back 400 if the project ID is other than long format
			response.setStatus(400)
			JSONObject errorMsg = new Utils().resolveException(e2, 400)
			render errorMsg as JSON
		} catch(Exception e3){
			// on any other exception render 500
			response.setStatus(500)
			JSONObject errorMsg = new Utils().resolveException(e3, 500)
			render errorMsg as JSON
		}
	}

	/**
	 * Get all annotations for a specific project and its images, 
	 * where a specific user has assigned one or more special terms.
	 *
	 * @return the annotations as JSON array
	 */
	def getAnnotationsByUser(){
		try {
			// ##################################################
			Cytomine cytomine = request['cytomine']
			String publicKey = params['publicKey']
			String privateKey = params['privateKey']

			long sessionID = params.long('sessionID')
			long projectID = params.long('cmProjectID')

			// ##################################################
			// default filter for a specific project/image/term
			Map<String,String> filters = new HashMap<String,String>()
			filters.put("project", String.valueOf(projectID))
			filters.put("showGIS", "true") // show the centroid information on the location
			filters.put("showMeta", "true") // show the meta informations
			filters.put("showTerm", "true") // show the term informations

			// if images == null, the parameter will be missing on the filter map
			// and this causes searching in the entire project (all images)
			String imageIDs = params['images']
			if (imageIDs != null && !imageIDs.equals("")){
				// if the image parameters is not null, put it on the filtermap
				filters.put("images", String.valueOf(imageIDs))
			}
			
			// #####################################################################################
			// NEVER RETRIEVE NOTERM=TRUE, BECAUSE THIS DOES NOT ENSURE THAT THIS USER DOES NOT HAVE
			// YET LABELED THE ANNOTATION, BUT JUST SAYS THAT THE ANNOTATION HAS NO LABEL AT ALL!
			// #####################################################################################

			// HINT: putting no "term" on the filter map retrieves all annotations for the selected images
			String termIDs = params['terms']
			boolean termsEmpty = (termIDs == null || termIDs.equals(""))
			// split the list in the params
			def queryTerms = String.valueOf(termIDs).split(",") as List
			
			boolean searchForNoTerm = false
			if (!termsEmpty){
				//  check if '-99' is on the list ('no terms assigned')
				if (queryTerms.contains('-99')){
					searchForNoTerm = true
					log.info("Querying all annotations and search for 'no term' (-99)...")
				} else {
					filters.put("terms", String.valueOf(termIDs))
					log.info("Querying selected terms...")
				}
			}

			// print the filtermap
			log.debug(filters)
			
			// get the session and the user
			Session sess = Session.get(sessionID)
			User user = sess.getUser()

			// the calling user has to be the owner of the session
			if (!(user.cmPublicKey == publicKey && user.cmPrivateKey == privateKey)){
				log.error("The calling user does not match the session owner.")
				throw new CytomineException(400, "The calling user does not match the session owner.")
			}
			<commit_msg>changed log level of some messages<commit_after>package be.cytomine.apps.iris

import grails.converters.JSON

import org.codehaus.groovy.runtime.typehandling.GroovyCastException;
import org.json.simple.JSONArray
import org.json.simple.JSONObject

import be.cytomine.client.Cytomine
import be.cytomine.client.CytomineException;
import be.cytomine.client.collections.AnnotationCollection;
import be.cytomine.client.collections.TermCollection;
import be.cytomine.client.models.Annotation;
import be.cytomine.client.models.AnnotationTerm;
import be.cytomine.client.models.ImageInstance;
import be.cytomine.client.models.Ontology
import be.cytomine.clientx.CytomineX;


/**
 * 
 * @author <NAME>
 *
 */
class AnnotationController {

	def grailsApplication
	def sessionService
	def annotationService

	def beforeInterceptor = {
		log.debug("Executing action $actionName with params $params")
	}

	/**
	 * Get all annotations for a specific project and image.
	 * 
	 * @return the annotations as JSON array
	 */
	def getAnnotations(){
		try {
			Cytomine cytomine = request['cytomine']
			String publicKey = params['publicKey']

			long sessionID = params.long('sessionID')
			long projectID = params.long('cmProjectID')

			// filter for a specific project/image/term
			Map<String,String> filters = new HashMap<String,String>()
			filters.put("project", String.valueOf(projectID))
			filters.put("showGIS", "true") // show the centroid information on the location
			filters.put("showMeta", "true") // show the meta informations
			filters.put("showTerm", "true") // show the term informations

			String imageIDs = params['images']
			if (imageIDs != null){
				filters.put("images", String.valueOf(imageIDs))
			}

			// filter for terms
			String termIDs = params['terms']
			if (termIDs != null){
				filters.put("terms", String.valueOf(termIDs))
			}
			// additionally check for termID 0, this will be annotations without terms
			if (params['noTerm'] != null && params['noTerm'].equals("true")){
				filters.put("noTerm", "true")
			}
			// check for multiple terms
			if (params['multipleTerm'] != null && params['multipleTerm'].equals("true")){
				filters.put("multipleTerm", "true")
			}

			// TODO implement pagination
			int max = (params['max']==null?0:params.int('max'))

			// get the session and the user
			Session sess = Session.get(sessionID)
			User user = sess.getUser()

			// find the project
			Project p = sess.getProjects().find { it.cmID == projectID }
			long ontologyID = p.getCmOntology();

			// fetch the terms from the ontology
			TermCollection terms = cytomine.getTermsByOntology(ontologyID)

			// get all annotations according to the filter
			AnnotationCollection annotations = cytomine.getAnnotations(filters)
			def irisAnnList = new JSONArray()

			// create a new domain mapper
			DomainMapper dm = new DomainMapper(grailsApplication)

			for(int i=0;i<annotations.size();i++) {
				Annotation annotation = annotations.get(i)

				// map the annotation to the IRIS model
				be.cytomine.apps.iris.Annotation irisAnn = dm.mapAnnotation(annotation, null)

				irisAnn = annotationService.resolveTerms(ontologyID, terms, user, annotation, irisAnn)

				// ######################################################################################
				// IMPORTANT: do NOT inject the cytomine annotation, because they contain information on
				// mappings done by other users
				// ######################################################################################
				// add the annotation to the result and use the AnnotationMarshaller in order to
				// serialize the domain objects
				irisAnnList.add(irisAnn)
			}

			render (irisAnnList as JSON)

		} catch(CytomineException e1){
			// exceptions from the cytomine java client
			response.setStatus(e1.httpCode)
			JSONObject errorMsg = new Utils().resolveCytomineException(e1)
			render errorMsg as JSON
		} catch(GroovyCastException e2) {
			// send back 400 if the project ID is other than long format
			response.setStatus(400)
			JSONObject errorMsg = new Utils().resolveException(e2, 400)
			render errorMsg as JSON
		} catch(Exception e3){
			// on any other exception render 500
			response.setStatus(500)
			JSONObject errorMsg = new Utils().resolveException(e3, 500)
			render errorMsg as JSON
		}
	}

	/**
	 * Get all annotations for a specific project and its images, 
	 * where a specific user has assigned one or more special terms.
	 *
	 * @return the annotations as JSON array
	 */
	def getAnnotationsByUser(){
		try {
			// ##################################################
			Cytomine cytomine = request['cytomine']
			String publicKey = params['publicKey']
			String privateKey = params['privateKey']

			long sessionID = params.long('sessionID')
			long projectID = params.long('cmProjectID')

			// ##################################################
			// default filter for a specific project/image/term
			Map<String,String> filters = new HashMap<String,String>()
			filters.put("project", String.valueOf(projectID))
			filters.put("showGIS", "true") // show the centroid information on the location
			filters.put("showMeta", "true") // show the meta informations
			filters.put("showTerm", "true") // show the term informations

			// if images == null, the parameter will be missing on the filter map
			// and this causes searching in the entire project (all images)
			String imageIDs = params['images']
			if (imageIDs != null && !imageIDs.equals("")){
				// if the image parameters is not null, put it on the filtermap
				filters.put("images", String.valueOf(imageIDs))
			}
			
			// #####################################################################################
			// NEVER RETRIEVE NOTERM=TRUE, BECAUSE THIS DOES NOT ENSURE THAT THIS USER DOES NOT HAVE
			// YET LABELED THE ANNOTATION, BUT JUST SAYS THAT THE ANNOTATION HAS NO LABEL AT ALL!
			// #####################################################################################

			// HINT: putting no "term" on the filter map retrieves all annotations for the selected images
			String termIDs = params['terms']
			boolean termsEmpty = (termIDs == null || termIDs.equals(""))
			// split the list in the params
			def queryTerms = String.valueOf(termIDs).split(",") as List
			
			boolean searchForNoTerm = false
			if (!termsEmpty){
				//  check if '-99' is on the list ('no terms assigned')
				if (queryTerms.contains('-99')){
					searchForNoTerm = true
					log.debug("Querying all annotations and search for 'no term' (-99)...")
				} else {
					filters.put("terms", String.valueOf(termIDs))
					log.debug("Querying selected terms...")
				}
			}

			// print the filtermap
			log.debug(filters)
			
			// get the session and the user
			Session sess = Session.get(sessionID)
			User user = sess.getUser()

			// the calling user has to be the owner of the session
			if (!(user.cmPublicKey == publicKey && user.cmPrivateKey == privateKey)){
				log.error("The calling user does not match the session owner.")
				throw new CytomineException(400, "The calling user does not match the session owner.")
			}
			
{'fraction_non_alphanumeric': 0.16534046898105767, 'fraction_numerical': 0.005210900640799944, 'mean_word_length': 3.8904958677685952, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '983826', 'n_tokens_mistral': 4730, 'n_tokens_neox': 3918, 'n_words': 1217}