<commit_before>TRANSACCIONALIDAD: TODAS las acciones publicas envuelven de una u otra manera el uso de una
  transaccion. Las acciones publicas son aquellas que en este documento estan prefijadas con
  un numero. Las acciones privadas, con una letra. LAS ACCIONES PUBLICAS NO DEBERIAN INVOCARSE
  ENTRE SI.
  Por ENVOLVER se entiende que el cuerpo de cada accion esta dentro de un bloque "with atomic()".

1. Como se crea una instancia de workflow.

   Parametros: Workflow a crear, documento para el cual crear el workflow, usuario.

   (!) Verificamos que el workflow tenga exactamente un curso de accion. Sin
       eso entonces no deberiamos permitir crear la instancia.
       [WorkflowHasMultipleMainCourses o WorkflowHasNoMainCourse]
   (!) Verificamos que, si se especifica permiso, el usuario cumpla ese permiso.
       El permiso se comprueba en el usuario y contra el workflow.
       [WorkflowCannotInstantiate]
   (.) Creamos la instancia de workflow.
   (!) Verificamos datos validos (workflow presente, documento presente).
       [ValidationError wrappeado]
   (!) Verificamos que la instancia de workflow logre "aceptar" el documento
       en base al workflow que le estamos dando.
       [ValidationError wrappeado]
   (.) Creamos la instancia de curso de accion del workflow, tomando como
       referencia el curso de accion principal de dicho workflow, y la
       instancia del workflow. El curso queda en estado pendiente ya que
       no sera iniciado automaticamente.

2. Como se inicia un curso de accion en una instancia de workflow.

   Parametros: codigo de curso de accion (en realidad, una cadena tipo codigo1.codigo2... ya
     que puede haber una cadena aciclica de cursos), usuario.
   (Contexto de objeto)

   (.) Obtenemos una instancia de curso de accion desde la instancia de workflow dada su
       cadena solicitada (inciso B).
   (!) El curso de accion tiene que estar pendiente.
       [WorkflowInstanceCourseNotPending]
   (!) Verificamos que el curso de accion tenga un (y solo un) nodo inicial.
       [ValidationError wrappeado]
   (!) Verificamos que el nodo inicial tenga una transicion, y esa transicion no sea
       a si mismo.
       [ValidationError wrappeado]
   (.) Tomamos nodo inicial. Tomamos su transicion.
   (!) Verificamos que, si la transicion requiere un permiso, entonces la instancia lo cumpla.
       El permiso se comprueba en el usuario y contra la instancia de workflow. Lo normal
       es que esa verificacion involucre al documento referenciado por la instancia de
       workflow, pero eso es harina de otro costal.
       [WorkflowInstanceTransitionDenied]
   (.) Ejecutamos transicion (inciso A).

3. Como se ejecuta un accion.

   Parametros: codigo de curso de accion (en realidad, una cadena tipo codigo1.codigo2... ya
     que puede haber una cadena aciclica de cursos), codigo de accion, usuario.
   (Contexto de objeto)

   (.) Obtenemos una instancia de curso de accion desde la instancia de workflow dada su
       cadena solicitada (inciso B).
   (!) Verificamos que la instancia del curso de accion este corriendo (ni pendiente, ni
       terminado, ni cancelado).
       [WorkflowInstanceCourseNotRunning]
   (!) Verificamos que la i. del curso de accion este parado en un nodo, y que ese nodo
       sea de tipo INPUT. Otros nodos revientan por los siguientes motivos:
       - Enter, Step, Multiplex: Estos nodos deberian ejecutarse automaticamente.
       - Cancel, Exit: Desde estos nodos no puede haber ejecucion.
       - Join: Este nodo tiene caminos paralelos dependientes que tienen que completarse.
       [ValidationError wrappeado]
   (!) Verificamos que las transiciones del nodo INPUT actual incluyan una con el nombre
       elegido.
       [WorkflowTransitionCodeDoesNotExist o WorkflowTransitionCodeNotUnique]
   (.) Tomamos esa transicion.
   (!) Verificamos que, si la transicion requiere un permiso, entonces la instancia lo cumpla.
       El permiso se comprueba en el usuario y contra la instancia de workflow. Lo normal
       es que esa verificacion involucre al documento referenciado por la instancia de
       workflow, pero eso es harina de otro costal.
       [WorkflowInstanceTransitionDenied]
   (.) Ejecutamos su transicion (inciso A).

4. Como se cancela un curso de accion.
   [TO-DO]

5. Como se deja un flujo en un estado consistente.
   [TO-DO]

A. Como se ejecuta una transicion dentro de una instancia de workflow.

   Parametros: transicion, instancia_curso, usuario.
   (Contexto de objeto)

   (*) Este metodo NO PROTEGE CONTRA BUCLES INFINITOS por lo que nuestro diseño debe
       ser lo suficientemente inteligente para no incurrir en uno de ellos.
   (*) Sabemos que la transicion es un objeto valido. Su origen es un objeto valido.
       Su nodo de inicio es un INPUT o un ENTER, y el permiso en tal caso ya fue
       verificado y aprobado antes de llegar a este punto (si es que hubo un permiso).
       Alternativamente, si cualquier otro tipo de nodo el origen, entonces esta
       invocacion se hizo con el fin de hacer que el curso se encuentre en un estado
       consistente (o sea resolviendo splits no resueltos, multiplexers, y pasos).
   (*) Sabemos que transicion e instancia_curso tienen referencia AL MISMO CURSO.
   (!) Verificamos que el origen y el destino sean ambos del mismo curso de accion.
       [ValidationError wrappeado]
   (!) Verificamos que el destino no sea ENTER ni CANCELADO.
       [ValidationError wrappeado]
   (.) nodo = Tomamos el nodo de destino de dicha transicion.
   (.) curso = Tomamos el curso de dicho nodo.
   (.) instancia_curso = Tomamos la instancia de curso en el objeto, para <curso>
   (.) SI nodo es tipo INPUT o EXIT:
       (.) curso.nodo = instancia de (nodo)
       (.) salvar curso.nodo y curso
       (.) DETENERNOS
           # Para el caso de EXIT, la sola presencia del nodo implica que
           # se encuentra terminado.
   (.) SI nodo es tipo SPLIT
       (!) Verificamos que el nodo tenga al menos dos ramas.
       [ValidationError wrappeado]
       instancia_nodo = instancia de (nodo)
       salvar instancia_nodo
       instancia_nodo.ramas = [instancia de (curso) por cada curso en nodo.ramas]
       # seran salvadas MIENTRAS se agregan
       DETENERNOS
   (.) SI nodo es tipo PASO:
       (.) Ejecutamos su callable. Si esto causa una excepcion, esa excepcion no
           sera atajada y seguira hacia arriba. El callable va a tomar solamente
           al documento y al usuario. NO va a tomar los objetos de workflow y NO
           se deberia trabajar con ellos durante este callable.
           Como estamos dentro de un contexto transaccional, vamos a volver a un
           estado consistente de todas maneras tras esta explosion.
       (!) Verificamos que el nodo tenga una y solo una transicion de salida.
           [ValidationError wrappeado]
       (.) transicion = tomamos esa transicion de salida.
       (.) LLAMADA RECURSIVA con transicion, instancia_curso, usuario.
   (.) SI nodo es tipo MULTIPLEXACION:
       (.)

B. Obtener una instancia de curso para una instancia de workflow y una cadena jerarquica.

   Parametros: cadena jerarquica.
   (Contexto de objeto).

   (!) Verificamos que la i. del workflow tenga exactamente una i. de curso de accion principal.
       Debe ser aquella cuyo curso base sea con depth=0.
       [WorkflowInstanceHasNoMainCourse]
   (.) Le pedimos esto a dicha instancia de curso (inciso C).

C. Obtener una instancia de curso para una instancia de curso raiz, y una cadena jerarquica.

   Parametros: cadena jerarquica.
   (Contexto de objeo).

   (.) SI la cadena esta vacia, devolvemos la instancia de curso actual.
   (.) SINO:
       (!) La instancia de nodo actual, en la instancia de curso actual, debe ser
           tipo split.
           [WorkflowInstanceCourseNodeDoesNotHaveChildren]
       (!) La instancia de nodo actual debe tener una instancia de nodo que pertenezca
           al mismo curso al que pertenece la instancia de curso que lo referencia.
           [WorkflowInstanceCourseNodeInconsistent]
       (.) Extraemos cabeza, cola de la lista, rompiendola segun ".".
           A toooodo esto, cabeza no sera vacia pero cola puede serlo.
       (!) El nodo referenciado por la instancia de nodo debe tener, entre sus branches,
           uno -y solo uno- cuyo codigo sea cabeza.
           [WorkflowCourseCodeDoesNotExist]
       (.) Tomamos ese nodo, y le pedimos que ejecute este inciso C, con la cola.<commit_msg>logic was defined for traversing multiplexers and splits. Additionally, joining a branch when using anticipated split resolution implies marking every sub-branch recursively as joined<commit_after>TRANSACCIONALIDAD: TODAS las acciones publicas envuelven de una u otra manera el uso de una
  transaccion. Las acciones publicas son aquellas que en este documento estan prefijadas con
  un numero. Las acciones privadas, con una letra. LAS ACCIONES PUBLICAS NO DEBERIAN INVOCARSE
  ENTRE SI.
  Por ENVOLVER se entiende que el cuerpo de cada accion esta dentro de un bloque "with atomic()".

1. Como se crea una instancia de workflow.

   Parametros: Workflow a crear, documento para el cual crear el workflow, usuario.

   (!) Verificamos que el workflow tenga exactamente un curso de accion. Sin
       eso entonces no deberiamos permitir crear la instancia.
       [WorkflowHasMultipleMainCourses o WorkflowHasNoMainCourse]
   (!) Verificamos que, si se especifica permiso, el usuario cumpla ese permiso.
       El permiso se comprueba en el usuario y contra el workflow.
       [WorkflowCannotInstantiate]
   (.) Creamos la instancia de workflow.
   (!) Verificamos datos validos (workflow presente, documento presente).
       [ValidationError wrappeado]
   (!) Verificamos que la instancia de workflow logre "aceptar" el documento
       en base al workflow que le estamos dando.
       [ValidationError wrappeado]
   (.) Creamos la instancia de curso de accion del workflow, tomando como
       referencia el curso de accion principal de dicho workflow, y la
       instancia del workflow. El curso queda en estado pendiente ya que
       no sera iniciado automaticamente.

2. Como se inicia un curso de accion en una instancia de workflow.

   Parametros: codigo de curso de accion (en realidad, una cadena tipo codigo1.codigo2... ya
     que puede haber una cadena aciclica de cursos), usuario.
   (Contexto de objeto)

   (.) Obtenemos una instancia de curso de accion desde la instancia de workflow dada su
       cadena solicitada (inciso B).
   (!) El curso de accion tiene que estar pendiente.
       [WorkflowInstanceCourseNotPending]
   (!) Verificamos que el curso de accion tenga un (y solo un) nodo inicial.
       [ValidationError wrappeado]
   (!) Verificamos que el nodo inicial tenga una transicion, y esa transicion no sea
       a si mismo.
       [ValidationError wrappeado]
   (.) Tomamos nodo inicial. Tomamos su transicion.
   (!) Verificamos que, si la transicion requiere un permiso, entonces la instancia lo cumpla.
       El permiso se comprueba en el usuario y contra la instancia de workflow. Lo normal
       es que esa verificacion involucre al documento referenciado por la instancia de
       workflow, pero eso es harina de otro costal.
       [WorkflowInstanceTransitionDenied]
   (.) Ejecutamos transicion (inciso A).

3. Como se ejecuta un accion.

   Parametros: codigo de curso de accion (en realidad, una cadena tipo codigo1.codigo2... ya
     que puede haber una cadena aciclica de cursos), codigo de accion, usuario.
   (Contexto de objeto)

   (.) Obtenemos una instancia de curso de accion desde la instancia de workflow dada su
       cadena solicitada (inciso B).
   (!) Verificamos que la instancia del curso de accion este corriendo (ni pendiente, ni
       terminado, ni cancelado).
       [WorkflowInstanceCourseNotRunning]
   (!) Verificamos que la i. del curso de accion este parado en un nodo, y que ese nodo
       sea de tipo INPUT. Otros nodos revientan por los siguientes motivos:
       - Enter, Step, Multiplex: Estos nodos deberian ejecutarse automaticamente.
       - Cancel, Exit: Desde estos nodos no puede haber ejecucion.
       - Join: Este nodo tiene caminos paralelos dependientes que tienen que completarse.
       [ValidationError wrappeado]
   (!) Verificamos que las transiciones del nodo INPUT actual incluyan una con el nombre
       elegido.
       [WorkflowTransitionCodeDoesNotExist o WorkflowTransitionCodeNotUnique]
   (.) Tomamos esa transicion.
   (!) Verificamos que, si la transicion requiere un permiso, entonces la instancia lo cumpla.
       El permiso se comprueba en el usuario y contra la instancia de workflow. Lo normal
       es que esa verificacion involucre al documento referenciado por la instancia de
       workflow, pero eso es harina de otro costal.
       [WorkflowInstanceTransitionDenied]
   (.) Ejecutamos su transicion (inciso A).

4. Como se cancela un curso de accion.
   [TO-DO]

5. Como se deja un flujo en un estado consistente.
   [TO-DO]

A. Como se ejecuta una transicion dentro de una instancia de workflow.

   Parametros: transicion, instancia_curso, usuario.
   (Contexto de objeto)

   (*) Este metodo NO PROTEGE CONTRA BUCLES INFINITOS por lo que nuestro diseño debe
       ser lo suficientemente inteligente para no incurrir en uno de ellos.
   (*) Sabemos que la transicion es un objeto valido. Su origen es un objeto valido.
       Su nodo de inicio es un INPUT o un ENTER, y el permiso en tal caso ya fue
       verificado y aprobado antes de llegar a este punto (si es que hubo un permiso).
       Alternativamente, si cualquier otro tipo de nodo el origen, entonces esta
       invocacion se hizo con el fin de hacer que el curso se encuentre en un estado
       consistente (o sea resolviendo splits no resueltos, multiplexers, y pasos).
   (*) Sabemos que transicion e instancia_curso tienen referencia AL MISMO CURSO.
   (!) Verificamos que el origen y el destino sean ambos del mismo curso de accion.
       [ValidationError wrappeado]
   (!) Verificamos que el destino no sea ENTER ni CANCELADO.
       [ValidationError wrappeado]
   (.) nodo = Tomamos el nodo de destino de dicha transicion.
   (.) curso = Tomamos instancia_curso.curso.
   (.) SI nodo es tipo INPUT o EXIT:
       (.) instancia_curso.nodo = instancia de (nodo)
       (.) salvar instancia_curso.nodo y instancia_curso
       (.) SI nodo es tipo INPUT
           (.) DETENERNOS
       (.) SI nodo es tipo EXIT
           (.) SI instancia_curso.padre
               Excitar joiner con instancia_curso, usuario, nodo.exit_value
           (.) SINO
               (.) DETENERNOS
   (.) SI nodo es tipo SPLIT
       (!) Verificamos que el nodo tenga al menos dos ramas.
       [ValidationError wrappeado]
       instancia_nodo = instancia de (nodo)
       salvar instancia_nodo
       instancia_nodo.ramas = [instancia de (curso) por cada curso en nodo.ramas]
       # seran salvadas MIENTRAS se agregan
       DETENERNOS
   (.) SI nodo es tipo PASO:
       (.) Ejecutamos su callable. Si esto causa una excepcion, esa excepcion no
           sera atajada y seguira hacia arriba. El callable va a tomar solamente
           al documento y al usuario. NO va a tomar los objetos de workflow y NO
           se deberia trabajar con ellos durante este callable.
           Como estamos dentro de un contexto transaccional, vamos a volver a un
           estado consistente de todas maneras tras esta explosion.
       (!) Verificamos que el nodo tenga una y solo una transicion de salida.
           [ValidationError wrappeado]
       (.) transicion = tomamos esa transicion de salida.
       (.) LLAMADA RECURSIVA con transicion, instancia_curso, usuario.
   (.) SI nodo es tipo MULTIPLEXACION:
       (.) Toma todas las transiciones, ordenadas crecientemente por prioridad.
       (!) Las transiciones deben tener prioridad y callable.
           [ValidationError wrappeado]
       (.) Las evalua en orden: de cada transicion toma su callable y lo invoca.
           Ese callable deberia invocarse tomando solamente al documento y al
           usuario. Si la condicion evalua un valor verdadero, nos quedamos
           con esa transicion y no con las siguientes.
       (!) Debemos tener, al final, alguna transicion.
           [WorkflowMultiplexerChoseNoTransition]
       (.) LLAMADA RECURSIVA con transicion (nueva), instancia_curso, usuario.

B. Obtener una instancia de curso para una instancia de workflow y una cadena jerarquica.

   Parametros: cadena jerarquica.
   (Contexto de objeto).

   (!) Verificamos que la i. del workflow tenga exactamente una i. de curso de accion principal.
       Debe ser aquella cuyo curso base sea con depth=0.
       [WorkflowInstanceHasNoMainCourse]
   (.) Le pedimos esto a dicha instancia de curso (inciso C).

C. Obtener una instancia de curso para una instancia de curso raiz, y una cadena jerarquica.

   Parametros: cadena jerarquica.
   (Contexto de objeo).

   (.) SI la cadena esta vacia, devolvemos la instancia de curso actual.
   (.) SINO:
       (!) La instancia de nodo actual, en la instancia de curso actual, debe ser
           tipo split.
           [WorkflowInstanceCourseNodeDoesNotHaveChildren]
       (!) La instancia de nodo actual debe tener una instancia de nodo que pertenezca
           al mismo curso al que pertenece la instancia de curso que lo referencia.
           [WorkflowInstanceCourseNodeInconsistent]
       (.) Extraemos cabeza, cola de la lista, rompiendola segun ".".
           A toooodo esto, cabeza no sera vacia pero cola puede serlo.
       (!) El nodo referenciado por la instancia de nodo debe tener, entre sus branches,
           uno -y solo uno- cuyo codigo sea cabeza.
           [WorkflowCourseCodeDoesNotExist]
       (.) Tomamos ese nodo, y le pedimos que ejecute este inciso C, con la cola.

D. Excitar un join de un nodo split superior, en una instancia de workflow.

   Parametros: instancia_curso, usuario, codigo de salida (>= 0 salida, -1 cancelacion).
   (Contexto de objeto)

   (*) Sabemos que existe instancia_curso.padre.
   (!) Verificamos que instancia_curso.padre.curso dentro de instancia_curso.curso.callers.
       [ValidationError wrappeado]
   (!) Verificamos que instancia_curso.padre sea un nodo SPLIT.
       [ValidationError wrappeado]
   (!) Verificamos que el nodo es consistente respecto de tener joiner y la
       cantidad de outbounds.
       [ValidationError wrappeado]
   (.) Tomamos el callable, y todas las transiciones.
       Si el callable es nulo, usamos un callable predeterminado que tiene
       en cuenta el codigo de la unica transicion presente, y lo devuelve cuando
       todas las instancias de curso que son ramas estan terminadas, mientras que
       devuelve nulo en los otros casos. Este callable devolvera None siempre que
       al menos una entrada del diccionario sea None.
   (.) De la instancia de nodo padre tomamos todas las instancias de curso que le son
       hijas.
   (!) Verificamos que todas las instancias de curso hijas correspondan con todas
       las ramas en el nodo split, consistentemente.
       [ValidationError wrappeado]
   (.) Armamos un diccionario codigo => exit_value con todas las instancias de
       cursos hijas donde codigo es el codigo del curso mientras que exit_value
       va a ser None (todavia esta corriendo), -1 (fue cancelado), o un numero
        >= 0 (fue terminada).
   (.) Tomamos instancia_curso.curso.codigo y tomamos exit_value.
   (.) Con esos tres parametros (diccionario, codigo, exit_value) invocamos el
       callable. Tomamos su valor.
   (.) Actualizamos el diccionario que hicimos, con [codigo] = exit_value
   (.) Evaluamos lo que devolvio el callable:
       (!) Solo se acepta None o algun string/unicode como valor de retorno.
           [WorkflowSplitResolutionBadValue]
       (.) None:
           (!) En el diccionario actualizado al menos una entrada tiene que
               quedar con None.
               [WorkflowSplitResolutionNeedsTransitionCode]
           DETENEMOS
       (.) Un string (o unicode):
           (!) Debe existir una transicion outbound dentro del split con ese
               codigo, y solo una.
               [WorkflowSplitResolutionTransitionCodeNotUnique o
                WorkflowSplitResolutionTransitionCodeDoesNotExist]
           (.) Tomamos esa transicion.
           (.) Para todas las claves en el diccionario que estan en None:
               (.) La marcamos en joined (inciso E).

E. Poner una instancia de curso en JOINED.

   Parametros: instancia de curso, usuario.

   (*) Sabemos que la instancia de curso a poner en joined es consistente.
   (!) Verificamos que la instancia hermana de la actual que lleva dicha clave
       por codigo, tenga un nodo joined.
   (.) Si la instancia de nodo actual en la instancia de curso referencia a un SPLIT:
       (.) Por cada instancia de curso hija:
           (.) RECURSIVAMENTE las vamos poniendo en joined.
   (.) Ponemos la instancia hermana de la actual con dicho codigo
       en su nodo de tipo JOINED.
{'fraction_non_alphanumeric': 0.052157847363683736, 'fraction_numerical': 0.0011369558008432422, 'mean_word_length': 2.9591147786946737, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 7, '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': '404814', 'n_tokens_mistral': 6491, 'n_tokens_neox': 6416, 'n_words': 2721}