+++ /dev/null
-== CREM data and how to import it into Telemeta ==
-
-Warning: the following instructions allow you to import the CREM's data
-into telemeta. However, it is currently a rather data destructive process,
-only meant for demonstration and testing purpose. Do not use this in a
-production environment.
-
-1 - Install Telemeta upon MySQL. Initialize the database using Telemeta's
-Django models. Ensure that everything is running fine before going any
-further.
-
-2 - Run prepare on the source directory. The source directory must contain
-text files as they were exported from the 4D database. These files are
-expected to be gzipped.
-
-Example:
-$ scripts/prepare src/2007-05-09
-
-3 - Use the import.sql script to insert the data into your MySQL database.
-
-Example:
-$ mysql your_telemeta_database < scripts/import.sql
-
-4 - Copy the WAV test file of your choice, into <MEDIA_ROOT>/items/test.wav
-This single file is associated with all media items, for testing purpose.
-
-That should be it. If you want to run Telemeta against SQLite instead of
-MySQL, first follow the above instructions to import the data into MySQL.
-Then convert your data from MySQL to SQLite, this is a common task, google
-about it.
-
-
-
--- /dev/null
+#!/bin/bash
+
+python parse.py
+cd build
+rst2html docref.txt docref.html && rst2latex docref.txt docref.tex && pdflatex docref.tex
+
--- /dev/null
+
+
+===================================================================
+Recommandations pour la réorganisation de la base de donnée du CREM
+===================================================================
+------------------------------------
+Document de référence, version alpha
+------------------------------------
+
+
+I - Enjeux
+==========
+
+I.1 - Recherche d'informations
+------------------------------
+
+L'analyse de la structure de données et la rencontre des personnes concernées par son usage
+permettent de délimiter des enjeux majeurs. Il apparaît notamment que le fond documentaire
+que constitue cette base n'est actuellement pas mis à profit comme il pourrait l'être. Cette
+base est largement sous-utilisée, les chercheurs, pour certains, ne l'utilisent pas du tout.
+
+La raison principale de cette situation est le fait que l'environnement technique et la
+structure de la base ont été conçus pour s'inscrire dans un environnement documentaire
+traditionnel, qui implique généralement de prendre contact avec un documentaliste pour effectuer
+une recherche. Le documentaliste est un spécialiste de la base, il possède les connaissances
+techniques nécessaires à sa consultation.
+
+Mais à l'heure d'Internet, ce processus de recherche est trop lourd et inadapté, en un mot
+: obsolète. Les chercheurs lui préfèrent d'autres fonds documentaires accessibles en ligne,
+de façon immédiate, et équipés d'outils de recherche puissants et ergonomiques.
+
+L'enjeu principal de la réorganisation de la base du CREM consiste donc à : sous-tendre le
+développement d'un outil moderne de gestion et de consultation en ligne, avec des fonctions
+de recherche puissantes et intuitives déterminer et améliorer les données de la base qui
+sont prioritaires pour la recherche d'information
+
+En de-ça des-dits champs prioritaires, il existe un grand nombre d'information techniques de
+second ordre, qui soit ne sont utiles qu'aux documentalistes, soit ne présentent pas d'intérêt
+majeur pour la recherche d'information. Ces informations peuvent être conservées telles quelles,
+sans grand effort de réorganisation, et resteront ainsi disponibles lors de la consultation
+individuelle des fiches.
+
+En procédant de cette façon, c'est à dire en délimitant les-dîtes priorités, il semble
+possible de dégager une véritable plus-value, de rendre le fond documentaire du CREM plus vivant,
+sans pour autant engager des moyens démesurés dans la réorganisation de la base de donnée.
+
+I.2 - Gestion et mise à jour des données
+----------------------------------------
+
+Il est entendu qu'au delà de la consultation d'information, il s'agit également de faciliter
+la gestion des données, leur mise à jour. Cette question implique pour une large part des
+problématiques d'ordre ergonomique, liées au développement d'un outil logiciel autour de la
+base, ce qui n'entre pas dans le cadre de la réorganisation de la base de données.
+
+Cependant, au niveau de la base, certains choix sont structurant. Par exemple l'emploi d'une
+liste hiérarchisée de lieux telle que le Thesaurus of Geographical Names (TGN, recommandé par
+Dublin Core), permet, en choisissant une région, une sous-région ou un village, de renseigner
+automatiquement le pays (et les autres zones géographiques intermédiaires), ce qui diminue
+l'effort et prévient les erreurs de saisie.
+
+Pour un grand nombre de champs, l'emploi d'énumérations (liste de valeurs valides pour
+un champ donné) facilite et valide déjà la mise à jour des données. Cet aspect de la base
+devra être conservé.
+
+Cependant, il semble que l'enjeu majeur de la réorganisation, du point de vue de la gestion
+des données, est de permettre un travail collaboratif, entre chercheurs et documentalistes. C'est
+là, d'une manière générale, ce qui permet sur Internet de rendre un fond documentaire vivant.
+
+Dans le cadre du CREM, il serait ainsi idéal d'amener les chercheurs à alimenter la base de
+façon autonome, en sus des documentalistes dont c'est le métier. Ce serait là une véritable
+nouveauté, représentant un fort potentiel d'enrichissement des données.
+
+D'une façon générale, une application en ligne ("webifiée") bien conçue se prête
+très bien au travail collaboratif. Cependant, il semble que dans notre cas, les potentielles
+contributions des chercheurs se heurtent à une question importante de confidentialité et
+de propriété intellectuelle. Des cas ont en effet été rapportés d'usage détournés du
+fruit du travail du centre de recherche, et d'une manière générale, il semble qu'il faille
+observer une grande prudence pour la mise en commun d'enregistrement sonores et autres données,
+qui représentent une matière précieuse aux yeux des chercheurs.
+
+La sécurité informatique est pour une large part distinct de la base de donnée ; elle
+implique des efforts particuliers au niveau du développement applicatif, et de l'administration
+de l'infrastructure réseau.
+
+Cependant, au niveau de la base de donnée, il est possible de mettre l'accent sur la
+propriété des données. Chaque ressource (collection, item) peut en effet se voir attribuer
+un propriétaire. Le dit propriétaire devrait ensuite pouvoir choisir quel autre utilisateur
+et/ou groupe d'utilisateur est autorisé à consulter, écouter, et/ou modifier la ressource.
+
+En rassurant ainsi les chercheurs, il est envisageable que la base s'enrichisse notablement
+par leurs contributions, dont certaines d'abord privées, pourront, à leur guise, devenir
+petit à petit accessibles à un nombre grandissant d'utilisateurs et de groupes de travail.
+
+I.3 - Données prioritaires
+
+Après consultation des différents utilisateurs il apparaît que les meta-données principales
+pour chaque item sont :
+
+* l'identifiant unique de l'item
+* le titre de l'item
+* la zone géographique
+* le nom du collecteur, c'est à dire le chercheur qui a collecté cet enregistrement
+* les instruments de musique utilisés
+* l'année d'enregistrement
+* le contexte ethnographique
+* l'ethnie
+
+Il s'agit d'apporter une attention particulière à celles-ci, notamment en les normalisant,
+pour faciliter la recherche d'informations, mais aussi pour l'ajout et la gestion simplifiés
+d'items.
+
+Les autres données pourront être conservées telles quelles sans grand effort de normalisation.
+
+I.4 - Dublin Core
+-----------------
+
+...
+
+II - Modalités de conversion/réorganisation de la base
+======================================================
+
+II.1 - Enumérations simples
+---------------------------
+
+dynamic:tables.describe_enums()
+
+II.2 - Collections
+------------------
+
+Ancien nom de table : support
+
+Nouveau nom de table : media_collections
+
+II.2.1 - Champs convertis
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynamic:tables.media_collections.describe_conversion()
+
+II.2.2 - Nouveaux champs
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynamic:tables.media_collections.describe_new_fields()
+
+II.2.3 - Champs supprimés
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynamic:tables.media_collections.describe_removed_fields()
+
+II.3 - Items
+------------
+
+Ancien nom de table : phono
+
+Nouveau nom de table : media_items
+
+II.3.1 - Champs convertis
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynamic:tables.media_items.describe_conversion()
+
+II.3.2 - Nouveaux champs
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynamic:tables.media_items.describe_new_fields()
+
+II.3.3 - Champs supprimés
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+dynamic:tables.media_items.describe_removed_fields()
+
+II.4 - Sélections
+-----------------
+
+II.4.1 - Liste
+~~~~~~~~~~~~~~
+
+Nom de table:
+dynamic:tables.media_playlists.name
+
+dynamic:tables.media_playlists.describe_new_fields()
+
+II.4.2 - Table de relation interne
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Nom de table:
+dynamic:tables.media_playlist_resources.name
+
+dynamic:tables.media_playlist_resources.describe_new_fields()
+
+II.5 - Thesaurus Géographique
+-----------------------------
+
+II.5.1 - Lieux
+~~~~~~~~~~~~~~
+
+Nom de table:
+dynamic:tables.locations.name
+
+dynamic:tables.locations.describe_new_fields()
+
+II.5.2 - Alias des lieux
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Nom de table:
+dynamic:tables.location_aliases.name
+
+dynamic:tables.location_aliases.describe_new_fields()
+
+II.5.3 - Relations hiérarchiques
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Nom de table:
+dynamic:tables.location_relations.name
+
+dynamic:tables.location_relations.describe_new_fields()
+
+II.6 - Utilisateurs
+-------------------
+
+Nom de table:
+dynamic:tables.users.name
+
+dynamic:tables.users.describe_new_fields()
--- /dev/null
+#coding: utf-8
+import sqlalchemy
+import texttable
+
+def make_table(rows, width=20):
+ return texttable.indent(rows, hasHeader = True, separateRows = True,
+ prefix=u'| ', postfix=u' |',
+ wrapfunc = lambda x: texttable.wrap_onspace(x, width=width))
+
+def describe_enums(metadata):
+ rows = [['Nom interne', 'Ancien nom', 'Description']]
+ for enum in metadata.enums:
+ rows.append([enum.name, enum.old_name, enum.label])
+ return make_table(rows, 40)
+
+class Column(sqlalchemy.Column):
+ old_name = ''
+ label = ''
+ desc = ''
+ dc = ''
+ comment = ''
+ conversion = ''
+ enum = None
+
+ def __init__(self, name, type, *args, **kwargs):
+ parent_args = {}
+ for key, value in kwargs.items():
+ if key == 'old_name':
+ if isinstance(value, list):
+ self.old_name = unicode(",\n".join(value), 'utf-8')
+ #self.old_name = []
+ #for item in value:
+ # self.old_name.append(unicode(item, 'utf-8'))
+ else:
+ self.old_name = unicode(value, 'utf-8')
+ elif key == 'label':
+ self.label = unicode(value, 'utf-8')
+ elif key == 'desc':
+ self.desc = unicode(value, 'utf-8')
+ elif key == 'dc':
+ self.dc = value
+ elif key == 'comment':
+ self.comment = unicode(value, 'utf-8')
+ elif key == 'conversion':
+ self.conversion = unicode(value, 'utf-8')
+ else:
+ parent_args[key] = value
+
+ if isinstance(type, Enumeration):
+ args = (sqlalchemy.ForeignKey(type.name + '.id'),) + args
+ self.enum = type
+ type = sqlalchemy.Integer
+
+ super(Column, self).__init__(name, type, *args, **parent_args)
+
+class RemovedColumn(object):
+ old_name = ''
+ comment = ''
+
+ def __init__(self, old_name, comment=''):
+ self.old_name = unicode(old_name, 'utf-8')
+ self.comment = unicode(comment, 'utf-8')
+
+class Table(sqlalchemy.Table):
+
+ def __init__(self, table_name, metadata, label, *args):
+ self.label = unicode(label, 'utf-8')
+ real_columns = []
+ self.removed_columns = []
+ for column in args:
+ if isinstance(column, RemovedColumn):
+ self.removed_columns.append(column)
+ else:
+ real_columns.append(column)
+ super(Table, self).__init__(table_name, metadata, *real_columns)
+
+
+ def describe_new_fields(self):
+ rows = [['Nom', 'Nom interne', 'Dublin Core']]
+ for column in self.columns:
+ if not column.old_name:
+ rows.append([column.label, column.name, column.dc])
+
+ return make_table(rows)
+
+ def describe_conversion(self):
+ rows = [['Nouveau nom', 'Ancien nom', 'Nouveau nom interne', 'Dublin Core']]
+ for column in self.columns:
+ if column.old_name:
+ rows.append([column.label, column.old_name, column.name, column.dc])
+
+ return make_table(rows)
+
+ def describe_removed_fields(self):
+ rows = [['Nom', 'Commentaire']]
+ for column in self.removed_columns:
+ rows.append([column.old_name, column.comment])
+
+ return make_table(rows, 50)
+
+ def to_dot(self):
+ dot = u'digraph g {\n'
+ dot += ' charset = "utf-8";\n'
+ dot += ' node [shape=record, charset="utf-8"];\n'
+ dot += ' rankdir = LR;\n'
+ dot += ' subgraph cluster_new_fields {\n'
+ dot += ' label = "Nouveaux champs";\n'
+ dot += ' color = black;\n'
+ old_fields = ''
+ conversion = ''
+ for column in self.columns:
+ dot += ' ' + column.name + '[label = "{' + column.label + ' | ' + column.name + '}"];\n'
+ if column.old_name:
+ old_fields += ' old_' + column.name + '[label = "' + column.old_name + '"];\n'
+ conversion += ' old_' + column.name + ' -> ' + column.name + ';\n'
+ dot += ' }\n'
+ dot += ' subgraph cluster_old_fields {\n'
+ dot += ' label = "Anciens champs";\n'
+ dot += ' color = black;\n'
+ dot += old_fields
+ dot += ' }\n'
+ dot += conversion
+ dot += '}\n'
+ return dot
+
+class Enumeration(Table):
+
+ def __init__(self, name, metadata, label='', old_name=''):
+ self.old_name = unicode(old_name, 'utf-8')
+ if not hasattr(metadata, 'enums'):
+ metadata.enums = []
+ metadata.enums.append(self)
+ super(Enumeration, self).__init__(name, metadata, label,
+ Column('id', sqlalchemy.Integer, primary_key=True),
+ Column('value', sqlalchemy.String(250), unique=True)
+ )
--- /dev/null
+
+import tables
+import codecs
+input = codecs.open('docref.txt', 'r', "utf-8")
+output = codecs.open('build/docref.txt', 'w', "iso-8859-1")
+
+for line in input:
+ if line[0:8] == 'dynamic:':
+ output.write(eval(line[8:], globals(), locals()) + "\n")
+ else:
+ output.write(line)
+
+input.close()
+output.close()
+
+output = codecs.open('build/collections.dot', 'w', 'utf-8')
+output.write(tables.media_collections.to_dot())
+output.close()
--- /dev/null
+#!python\r
+\r
+## The MIT License\r
+\r
+## Copyright (c) <year> <copyright holders>\r
+\r
+## Permission is hereby granted, free of charge, to any person obtaining a copy\r
+## of this software and associated documentation files (the "Software"), to deal\r
+## in the Software without restriction, including without limitation the rights\r
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+## copies of the Software, and to permit persons to whom the Software is\r
+## furnished to do so, subject to the following conditions:\r
+\r
+## The above copyright notice and this permission notice shall be included in\r
+## all copies or substantial portions of the Software.\r
+\r
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+## THE SOFTWARE.\r
+\r
+\r
+from sqlalchemy import types, exceptions\r
+\r
+class StaticEnum(types.TypeDecorator):\r
+ impl = types.Unicode\r
+ \r
+ def __init__(self, values, empty_to_none=False, strict=False):\r
+ """Emulate an Enum type.\r
+\r
+ values:\r
+ A list of valid values for this column\r
+ empty_to_none:\r
+ Optional, treat the empty string '' as None\r
+ strict:\r
+ Also insist that columns read from the database are in the\r
+ list of valid values. Note that, with strict=True, you won't\r
+ be able to clean out bad data from the database through your\r
+ code.\r
+ """\r
+\r
+ if values is None or len(values) is 0:\r
+ raise exceptions.AssertionError('StaticEnum requires a list of values')\r
+ self.empty_to_none = empty_to_none\r
+ self.strict = strict\r
+ self.values = values[:]\r
+\r
+ # The length of the string/unicode column should be the longest string\r
+ # in values\r
+ size = max([len(v) for v in values if v is not None])\r
+ super(StaticEnum, self).__init__(size) \r
+ \r
+ \r
+ def convert_bind_param(self, value, engine):\r
+ if self.empty_to_none and value is '':\r
+ value = None\r
+ if value not in self.values:\r
+ raise exceptions.AssertionError('"%s" not in StaticEnum.values' % value)\r
+ return super(StaticEnum, self).convert_bind_param(value, engine)\r
+ \r
+ \r
+ def convert_result_value(self, value, engine):\r
+ if self.strict and value not in self.values:\r
+ raise exceptions.AssertionError('"%s" not in StaticEnum.values' % value)\r
+ return super(StaticEnum, self).convert_result_value(value, engine)\r
+\r
--- /dev/null
+#coding: utf-8
+
+import sqlalchemy
+from sqlalchemy import String, Integer, ForeignKey, Time, Date, MetaData, Text, Boolean
+from elements import Column, RemovedColumn, Table, Enumeration
+import elements
+from staticenum import StaticEnum
+
+metadata = MetaData()
+def describe_enums():
+ return elements.describe_enums(metadata)
+
+# Users
+
+users = Table('users', metadata, 'Utilisateurs',
+ Column('username', String(250), primary_key=True, label='Nom d\'utilisateur'),
+ Column('level', StaticEnum('user,maintainer,administrator'), label='Niveau de permissions'),
+ Column('first_name', String(250), label='Prénom'),
+ Column('last_name', String(250), label='Nom'),
+ Column('phone', String(250), label='Téléphone'),
+ Column('email', String(250), label='E-Mail')
+)
+
+# Playlists
+
+media_playlists = Table('playlists', metadata, 'Sélections personelles d\'items et de collections',
+ Column('id', Integer, primary_key='true', label='Identifiant'),
+ Column('owner_username', String(250), ForeignKey('users.username'), label='Propriétaire'),
+ Column('name', String(250), label='Intitulé'))
+
+media_playlist_resources = Table('playlist_resources', metadata, 'Ressources associées aux sélections personelles ',
+ Column('playlist_id', Integer, ForeignKey('playlists.id'), label='Identifiant de la sélection'),
+ Column('resource_type', StaticEnum('item', 'collection'), label='Type de ressource (item, collection)'),
+ Column('resource_id', String(250), label='Identifiant de la ressource')
+)
+
+# Simple enumerations
+
+physical_formats = Enumeration('physical_formats', metadata, 'Formats physiques', old_name='Format (e)')
+publishing_status = Enumeration('publishing_status', metadata, 'Status d\'édition/réédition', old_name='Réédition (e)')
+publishers = Enumeration('publishers', metadata, 'Editeurs', old_name='Editeur1 (e)')
+acquisition_modes = Enumeration('acquisition_modes', metadata, 'Modes d\'acquisition', old_name='Mode_Acqui (e)')
+metadata_authors = Enumeration('record_authors', metadata, 'Rédacteurs des fiches', old_name='Rédacteur_Fiche (e)')
+metadata_writers = Enumeration('metadata_writers', metadata, 'Opérateur de saisie des fiches', old_name='Saisie_Fiche (e)')
+legal_rights = Enumeration('legal_rights', metadata, 'Statuts juridiques', old_name='Droit_d\'Utiliser (e)')
+recording_contexts = Enumeration('recording_contexts', metadata, 'Contextes d\'enregistrement', old_name='Terrain_ou_Autre (e)')
+ad_conversions = Enumeration('ad_conversions', metadata, 'Statuts de numérisation', old_name='Numérisation (e)')
+ethnic_groups = Enumeration('ethnic_groups', metadata, 'Ethnies/Groupe social', old_name='Ethnie (t)')
+vernacular_styles = Enumeration('vernacular_styles', metadata, 'Forme / genre vernaculaire', old_name='Form (t)')
+generic_styles = Enumeration('generic_styles', metadata, 'Forme / genre générique', old_name='FormStyle générique (e)')
+context_keywords = Enumeration('context_keywords', metadata, 'Mots clés du contexte ethnographique', old_name='Mot_Clef (t)')
+publisher_collections = Enumeration('publisher_collections', metadata, 'Collections éditeur', old_name='Collection_Série (e)')
+
+# Geographic Thesaurus
+
+location_types = Enumeration('location_types', metadata, 'GeoEthno / Types de lieux')
+
+locations = Table('locations', metadata, 'GeoEthno / Lieux)',
+ Column('name', String(127), primary_key=True, label='Terme descripteur'),
+ Column('type', StaticEnum('country', 'continent', 'other')),
+ Column('complete_type', location_types),
+ Column('current_name', String(127), ForeignKey('locations.name'), label='Nom actuel'),
+ Column('is_authoritative', Boolean),
+)
+
+location_aliases = Table('location_aliases', metadata, 'GeoEthno / Alias des lieux',
+ Column('location_name', String(127), ForeignKey('locations.name'), primary_key=True),
+ Column('alias', String(127), primary_key=True),
+ Column('is_authoritative', Boolean))
+
+location_relations = Table('location_relations', metadata, 'GeoEthno / Relations hiérachiques',
+ Column('location_name', String(127), ForeignKey('locations.name'), primary_key=True),
+ Column('parent_location_name', String(127), ForeignKey('locations.name'), primary_key=True)
+)
+
+# Media Collections
+
+media_collections = Table('media_collections', metadata, 'Collections',
+ Column('reference',
+ String(250), unique=True,
+ old_name='Réf', label='Référence'),
+ Column('physical_format_id',
+ physical_formats,
+ old_name='Format', label='Format',
+ desc="Format du 1er exemplaire archivé"),
+ Column('old_id',
+ String(250),
+ old_name='Cote', label='Ancienne cote'),
+ Column('id',
+ String(250), primary_key=True,
+ dc='identifier',
+ label='Cote', conversion='à préciser'),
+ Column('title',
+ String(250),
+ dc='title',
+ old_name='Titre', label='Titre'),
+ Column('native_title',
+ String(250),
+ dc='title',
+ old_name='Transcrip_Trad', label='Traduction du titre'),
+ Column('physical_items_num',
+ Integer,
+ old_name='Nb_de_pieces', label='Nombre de supports physiques'),
+ Column('publishing_status_id',
+ publishing_status,
+ old_name='Réédition', label='Réédition'),
+ RemovedColumn(old_name='Original'),
+ RemovedColumn(old_name='Copie_TotPartie'),
+ RemovedColumn(old_name='Copié_de'),
+ Column('creator',
+ String(250),
+ dc='creator',
+ old_name='Auteur_Compil', label='Auteur / Cédant'),
+ Column('booklet_author',
+ String(250),
+ dc='contributor',
+ old_name='Auteur_Notice', label='Auteur notice'),
+ Column('booklet_description',
+ Text,
+ old_name='Notice', label='Notice / Dossier technique'),
+ Column('collector',
+ Text,
+ dc='contributor',
+ old_name='Collecteur', label='Collecteur'),
+ Column('publisher_id',
+ publishers,
+ dc='publisher',
+ old_name='Editeur', label='Editeur',
+ desc='Pour les documents ÉDITÉS:Nom et État de l\'Editeur. Pour les INÉDITS :voir champ "Type de document".'),
+ Column('year_published',
+ Integer,
+ dc='date',
+ old_name='Année_parution', label='Année de parution',
+ desc='Ne concerne que les documents ÉDITÉS.',
+ conversion='à préciser, traiter les nombres négatifs ?'),
+ Column('publisher_collection',
+ publisher_collections,
+ old_name='Collect_Série', label='Collection éditeur',
+ comment='faux: nom de la collection, suivi du n° dans la collection.'),
+ Column('publisher_serial',
+ String(250),
+ old_name='Num_Dans_Collec', label='Numéro de série',
+ desc='Numéro de série dans la collection éditeur',
+ comment='à valider'),
+ Column('external_references',
+ Text,
+ old_name='Réf_Biblio', label='Bibliographie',
+ desc='Références biblio/disco/filmographiques, uniquement liées à ce support'),
+ Column('acquisition_mode_id',
+ acquisition_modes,
+ old_name='Mod_Acqui', label='Mode d\'acquisition'),
+ Column('comment',
+ Text,
+ old_name='Commentaire', label='Commentaire'),
+ Column('metadata_author_id',
+ metadata_authors,
+ dc='contributor',
+ old_name='Rédacteur_Fiche', label='Rédacteur fiche',
+ desc='Responsable de l\'analyse documentaire'),
+ Column('metadata_writer_id',
+ metadata_writers,
+ old_name='Saisie_Fiche', label='Saisie fiches',
+ desc='Personne qui a saisi les fiches dans la base de données.'),
+ Column('legal_rights_id',
+ legal_rights,
+ dc='rights',
+ old_name='Droit_Utiliser', label='Statut juridique'),
+ Column('alt_ids',
+ String(250),
+ old_name='Autres_Cotes', label='Autres exemplaires'),
+ Column('recorded_from_year',
+ Integer,
+ dc='date',
+ old_name='Année_Enreg', label='Années d\'enregistrement',
+ conversion="split"),
+ Column('recorded_to_year',
+ Integer,
+ dc='date',
+ old_name='Année_Enreg', label='Années d\'enregistrement',
+ conversion="split"),
+ Column('recording_context_id',
+ recording_contexts,
+ old_name='Terrain_ou_Autr', label='Contexte d\'enregistrement'),
+ Column('approx_duration',
+ Time(),
+ old_name='Durée_approx', label='Durée approximative'),
+ Column('doctype_code',
+ Integer,
+ old_name='Tri_DiBm', label='Type de document'),
+ Column('travail',
+ String(250),
+ old_name='Travail', label='?'),
+ Column('state',
+ String(250),
+ old_name='Compil_Face_Plage', label="Etat"),
+ Column('cnrs_contributor',
+ String(250),
+ old_name='Déposant_CNRS', label="Déposant CNRS",
+ desc='Pour les INÉDITS uniquement. Signale les collectes ayant bénéficées d\'une aide du CNRS.'),
+ Column('items_done',
+ String(250),
+ old_name='Fiches', label='Fiches faîtes',
+ desc="Signale que les fiches Item de ce support sont présentes dans la base."),
+ Column('a_informer_07_03',
+ String(250),
+ old_name='A informer_07-03', label='?'),
+ Column('ad_conversion_id',
+ ad_conversions,
+ old_name='Numérisation', label="Historique de numérisation"),
+ RemovedColumn(old_name='Champ36'),
+)
+
+# Media Items
+
+media_item_context_keywords = Table('media_item_context_keywords', metadata,
+ 'Mots clés associés à un item',
+ Column('media_item_id', Integer, ForeignKey('media_items.id')),
+ Column('context_keyword_id', context_keywords))
+
+media_items = Table('media_items', metadata, 'Items',
+ RemovedColumn(old_name='Réf', comment='Calculé en temps réel à partir de la collection'),
+ Column('collection_id',
+ String(250), ForeignKey('media_collections.id'),
+ dc='relation/isPartOf',
+ label='Collection'),
+ RemovedColumn(old_name='Format', comment='Calculé en temps réel à partir de la collection'),
+ RemovedColumn(old_name='Cote_Support', comment='Calculé en temps réel à partir de la collection'),
+ Column('track',
+ String(250),
+ old_name="Face_Plage", label="N° de l'item"),
+ Column('id',
+ String(250),
+ old_name='Cote_Phono', label="Cote item"),
+ Column('approx_duration',
+ Time(),
+ old_name='Durée', label='Durée'),
+ Column('recorded_from_date',
+ Date(),
+ dc='date',
+ old_name='Date_enregistr', label='Date', desc='Date de l\'enregistrement'),
+ Column('recorded_to_date',
+ Date(),
+ dc='date',
+ old_name='Date_enregistr', label='Date'),
+ Column('location_name',
+ Integer, ForeignKey('locations.name'),
+ dc='coverage',
+ old_name=['Continent', 'Etat', 'Région_Village'], label='Lieu',
+ desc='(?) Lieu de provenance de la musique. Si le lieu de l\'enregistrement est autre, l\'indiquer en "Remarques".'),
+ Column('location_comment',
+ String(250),
+ old_name='Région_Village', label='Commentaire lieu'),
+ Column('ethnic_group_id',
+ ethnic_groups,
+ old_name='Ethnie_GrSocial', label='Ethnie/Groupe social', comment='attention alias ethnies'),
+ RemovedColumn(old_name='Titre_Support', comment='Calculé en temps réel à partir de la collection'),
+ Column('title',
+ String(250),
+ dc='title',
+ old_name='Titre_pièce', label='Titre Item'),
+ Column('native_title',
+ String(250),
+ dc='title',
+ old_name='Transcrip_Trad', label='Traduction du titre',
+ desc='Traduction des langues non communes, plus translittération des langues n\'utilisant pas l\'alphabet latin.'),
+ Column('author',
+ String(250),
+ dc='creator',
+ old_name='Auteur', label='Auteur',
+ desc='Le cas échéant, faire suivre le nom par une mention "-auteur /-compositeur / -arrangeur"'),
+ Column('vernacular_style_id',
+ vernacular_styles,
+ old_name='Form_Genr_Style', label='Forme / genre vernaculaire [nom à revoir]',
+ desc='Nom local de la forme ou du genre'),
+ RemovedColumn(old_name='Struct_Modale'),
+ RemovedColumn(old_name='Struct_Rythm'),
+ RemovedColumn(old_name='Struct_Rythm'),
+ RemovedColumn(old_name='Fonction_Usage',
+ comment='Champ inutile, les mots clés sont associés via une table de relation externe'),
+ Column('context_comment',
+ Text,
+ old_name='Comm_FonctUsage', label='Contexte ethnographique : commentaires'),
+ Column('external_references',
+ String(250),
+ old_name='Documentation', label='Références',
+ desc='Références directement liées à l\'item.'),
+ Column('moda_execut',
+ String(250),
+ old_name='Moda_Execut', comment='à supprimer ?'),
+ Column('copied_from_item_id',
+ String(250), ForeignKey('media_items.id'),
+ dc='relation/isVersionOf',
+ old_name='Copie_de', label='Copie de'),
+ Column('collector',
+ String(250),
+ dc='contributor',
+ old_name='Enregistré_par', label='Collecteur'),
+ Column('cultural_area',
+ String(250),
+ old_name='Aire culturelle', label='Aire culturelle'),
+ RemovedColumn(old_name='Année_Enreg',
+ comment='calculé en temps-réel à partir de la date d\'enregistrement'),
+ Column('generic_style',
+ generic_styles,
+ old_name='FormStyl généri', label='Forme / genre générique'),
+ Column('collector_selection',
+ String(250),
+ old_name='ChoixCollecteur', label='Choix du collecteur',
+ desc='Permet au collecteur de repérer les items les plus intéressants'),
+ RemovedColumn(old_name='Repère_bande'),
+ Column('creator_reference',
+ String(250),
+ old_name='NroBandNroPièc', label='Référence du déposant'),
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+#coding: utf-8
+import cStringIO,operator
+
+def indent(rows, hasHeader=False, headerChar=u'-', delim=u' | ', justify=u'left',
+ separateRows=False, prefix=u'', postfix=u'', wrapfunc=lambda x:x):
+ """Indents a table by column.
+ - rows: A sequence of sequences of items, one sequence per row.
+ - hasHeader: True if the first row consists of the columns' names.
+ - headerChar: Character to be used for the row separator line
+ (if hasHeader==True or separateRows==True).
+ - delim: The column delimiter.
+ - justify: Determines how are data justified in their column.
+ Valid values are 'left','right' and 'center'.
+ - separateRows: True if rows are to be separated by a line
+ of 'headerChar's.
+ - prefix: A string prepended to each printed row.
+ - postfix: A string appended to each printed row.
+ - wrapfunc: A function f(text) for wrapping text; each element in
+ the table is first wrapped by this function."""
+ # closure for breaking logical rows to physical, using wrapfunc
+ def rowWrapper(row):
+ newRows = [wrapfunc(item).split('\n') for item in row]
+ return [[substr or '' for substr in item] for item in map(None,*newRows)]
+ # break each logical row into one or more physical ones
+ logicalRows = [rowWrapper(row) for row in rows]
+ # columns of physical rows
+ columns = map(None,*reduce(operator.add,logicalRows))
+ # get the maximum of each column by the string length of its items
+ maxWidths = [max([len(item) for item in column]) for column in columns]
+ rowSeparator = u"+" + u"+".join([u'-' * (width + len(delim) - 1) for width in maxWidths]) + u"+"
+ headerSep = u"+" + u"+".join([u'=' * (width + len(delim) - 1) for width in maxWidths]) + u"+"
+ #headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
+ # len(delim)*(len(maxWidths)-1))
+ #headerSep = u"=" * (len(prefix) + len(postfix) + sum(maxWidths) + \
+ # len(delim)*(len(maxWidths)-1))
+ # select the appropriate justify method
+ #justify = {'center':str.center, 'right':str.rjust, 'left':str.ljust}[justify.lower()]
+ output=u''
+ if separateRows:
+ output += rowSeparator + "\n"
+
+ for physicalRows in logicalRows:
+ for row in physicalRows:
+ output += \
+ prefix \
+ + delim.join([item.ljust(width) for (item,width) in zip(row,maxWidths)]) \
+ + postfix + "\n"
+ if hasHeader: output += headerSep + "\n"; hasHeader=False
+ elif separateRows: output += rowSeparator + "\n"
+ return output
+
+# written by Mike Brown
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
+def wrap_onspace(text, width):
+ """
+ A word-wrap function that preserves existing line breaks
+ and most spaces in the text. Expects that existing line
+ breaks are posix newlines (\n).
+ """
+ return reduce(lambda line, word, width=width: '%s%s%s' %
+ (line,
+ ' \n'[(len(line[line.rfind('\n')+1:])
+ + len(word.split('\n',1)[0]
+ ) >= width)],
+ word),
+ text.split(' ')
+ )
+
+import re
+def wrap_onspace_strict(text, width):
+ """Similar to wrap_onspace, but enforces the width constraint:
+ words longer than width are split."""
+ wordRegex = re.compile(r'\S{'+str(width)+r',}')
+ return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width)
+
+import math
+def wrap_always(text, width):
+ """A simple word-wrap function that wraps text on exactly width characters.
+ It doesn't split the text in words."""
+ return '\n'.join([ text[width*i:width*(i+1)] \
+ for i in xrange(int(math.ceil(1.*len(text)/width))) ])
+
+if __name__ == '__main__':
+ labels = ('First Name', 'Last Name', 'Age', 'Position')
+ data = \
+ '''John,Smith,24,Software Engineer
+ Mary,Brohowski,23,Sales Manager
+ Aristidis,Papageorgopoulos,28,Senior Reseacher'''
+ rows = [row.strip().split(',') for row in data.splitlines()]
+
+ print 'Without wrapping function\n'
+ print indent([labels]+rows, hasHeader=True)
+ # test indent with different wrapping functions
+ width = 10
+ for wrapper in (wrap_always,wrap_onspace,wrap_onspace_strict):
+ print 'Wrapping function: %s(x,width=%d)\n' % (wrapper.__name__,width)
+ print indent([labels]+rows, hasHeader=True, separateRows=True,
+ prefix='| ', postfix=' |',
+ wrapfunc=lambda x: wrapper(x,width))
+
+ # output:
+ #
+ #Without wrapping function
+ #
+ #First Name | Last Name | Age | Position
+ #-------------------------------------------------------
+ #John | Smith | 24 | Software Engineer
+ #Mary | Brohowski | 23 | Sales Manager
+ #Aristidis | Papageorgopoulos | 28 | Senior Reseacher
+ #
+ #Wrapping function: wrap_always(x,width=10)
+ #
+ #----------------------------------------------
+ #| First Name | Last Name | Age | Position |
+ #----------------------------------------------
+ #| John | Smith | 24 | Software E |
+ #| | | | ngineer |
+ #----------------------------------------------
+ #| Mary | Brohowski | 23 | Sales Mana |
+ #| | | | ger |
+ #----------------------------------------------
+ #| Aristidis | Papageorgo | 28 | Senior Res |
+ #| | poulos | | eacher |
+ #----------------------------------------------
+ #
+ #Wrapping function: wrap_onspace(x,width=10)
+ #
+ #---------------------------------------------------
+ #| First Name | Last Name | Age | Position |
+ #---------------------------------------------------
+ #| John | Smith | 24 | Software |
+ #| | | | Engineer |
+ #---------------------------------------------------
+ #| Mary | Brohowski | 23 | Sales |
+ #| | | | Manager |
+ #---------------------------------------------------
+ #| Aristidis | Papageorgopoulos | 28 | Senior |
+ #| | | | Reseacher |
+ #---------------------------------------------------
+ #
+ #Wrapping function: wrap_onspace_strict(x,width=10)
+ #
+ #---------------------------------------------
+ #| First Name | Last Name | Age | Position |
+ #---------------------------------------------
+ #| John | Smith | 24 | Software |
+ #| | | | Engineer |
+ #---------------------------------------------
+ #| Mary | Brohowski | 23 | Sales |
+ #| | | | Manager |
+ #---------------------------------------------
+ #| Aristidis | Papageorgo | 28 | Senior |
+ #| | poulos | | Reseacher |
+ #---------------------------------------------
+
--- /dev/null
+== CREM data and how to import it into Telemeta ==
+
+Warning: the following instructions allow you to import the CREM's data
+into telemeta. However, it is currently a rather data destructive process,
+only meant for demonstration and testing purpose. Do not use this in a
+production environment.
+
+1 - Install Telemeta upon MySQL. Initialize the database using Telemeta's
+Django models. Ensure that everything is running fine before going any
+further.
+
+2 - Run prepare on the source directory. The source directory must contain
+text files as they were exported from the 4D database. These files are
+expected to be gzipped.
+
+Example:
+$ scripts/prepare src/2007-05-09
+
+3 - Use the import.sql script to insert the data into your MySQL database.
+
+Example:
+$ mysql your_telemeta_database < scripts/import.sql
+
+4 - Copy the WAV test file of your choice, into <MEDIA_ROOT>/items/test.wav
+This single file is associated with all media items, for testing purpose.
+
+That should be it. If you want to run Telemeta against SQLite instead of
+MySQL, first follow the above instructions to import the data into MySQL.
+Then convert your data from MySQL to SQLite, this is a common task, google
+about it.
+
+
+
--- /dev/null
+DELETE FROM telemeta_collection;
+LOAD DATA INFILE '/tmp/crem_import/support.txt' INTO TABLE telemeta_collection
+FIELDS TERMINATED BY '\t'
+LINES TERMINATED BY '\r\n';
+
+UPDATE telemeta_collection SET id=REPLACE(id, '/', '--');
+UPDATE telemeta_collection SET copied_from_id = NULL WHERE copied_from_id = '';
+
+DELETE FROM telemeta_item;
+LOAD DATA INFILE '/tmp/crem_import/phono.txt' INTO TABLE telemeta_item
+FIELDS TERMINATED BY '\t'
+LINES TERMINATED BY '\r\n';
+
+UPDATE telemeta_item SET id=REPLACE(id, ":", "__");
+UPDATE telemeta_item SET id=REPLACE(id, '/', '--');
+UPDATE telemeta_item SET id=REPLACE(id, '?', 'i');
+UPDATE telemeta_item SET collection_id=REPLACE(collection_id, '/', '--');
+UPDATE telemeta_item SET title='' WHERE title='N';
+UPDATE telemeta_item SET etat=REPLACE(etat, ')', '_');
+UPDATE telemeta_item SET etat=REPLACE(etat, '(', '_');
+UPDATE telemeta_item SET file="items/test.wav";
+
+DELETE FROM telemeta_physicalformat;
+INSERT INTO telemeta_physicalformat (value)
+ SELECT DISTINCT physical_format FROM telemeta_collection
+ WHERE physical_format <> '' AND physical_format IS NOT NULL;
+
+DELETE FROM telemeta_publishingstatus;
+INSERT INTO telemeta_publishingstatus (value)
+ SELECT DISTINCT publishing_status FROM telemeta_collection
+ WHERE publishing_status <> '' AND publishing_status IS NOT NULL;
--- /dev/null
+#!/bin/bash
+
+if [ "$1" == "" ]
+then
+ echo "Please provide the source directory"
+ exit 1
+fi
+
+src=$1
+tmpdir=crem_import
+required="support.txt.gz phono.txt.gz"
+
+for f in $required
+do
+ if ! [ -f $src/$f ]
+ then
+ echo "Can't find $f in $src"
+ exit 1
+ fi
+done
+
+if [ -d /tmp/$tmpdir ]
+then
+ rm /tmp/$tmpdir/*
+else
+ mkdir /tmp/$tmpdir
+fi
+
+echo -n "Converting charset and cleaning text files.. "
+
+zcat $src/support.txt.gz | tail -n +2 \
+ | sed 's/^ *//' | sed 's/ *\t */\t/g'| sed 's/ *$//' \
+ | iconv -f WINDOWS-1252 -c -t latin1 \
+ > /tmp/$tmpdir/support.txt
+
+zcat $src/phono.txt.gz | tail -n +2 \
+ | sed 's/^ *//' | sed 's/ *\t */\t/g'| sed 's/ *$//' \
+ | iconv -f WINDOWS-1252 -c -t latin1 | sed '/^\t/d' \
+ > /tmp/$tmpdir/phono.txt
+
+echo "Done"
+
+
+++ /dev/null
-DELETE FROM telemeta_collection;
-LOAD DATA INFILE '/tmp/crem_import/support.txt' INTO TABLE telemeta_collection
-FIELDS TERMINATED BY '\t'
-LINES TERMINATED BY '\r\n';
-
-UPDATE telemeta_collection SET id=REPLACE(id, '/', '--');
-UPDATE telemeta_collection SET copied_from_id = NULL WHERE copied_from_id = '';
-
-DELETE FROM telemeta_item;
-LOAD DATA INFILE '/tmp/crem_import/phono.txt' INTO TABLE telemeta_item
-FIELDS TERMINATED BY '\t'
-LINES TERMINATED BY '\r\n';
-
-UPDATE telemeta_item SET id=REPLACE(id, ":", "__");
-UPDATE telemeta_item SET id=REPLACE(id, '/', '--');
-UPDATE telemeta_item SET id=REPLACE(id, '?', 'i');
-UPDATE telemeta_item SET collection_id=REPLACE(collection_id, '/', '--');
-UPDATE telemeta_item SET title='' WHERE title='N';
-UPDATE telemeta_item SET etat=REPLACE(etat, ')', '_');
-UPDATE telemeta_item SET etat=REPLACE(etat, '(', '_');
-UPDATE telemeta_item SET file="items/test.wav";
-
-DELETE FROM telemeta_physicalformat;
-INSERT INTO telemeta_physicalformat (value)
- SELECT DISTINCT physical_format FROM telemeta_collection
- WHERE physical_format <> '' AND physical_format IS NOT NULL;
-
-DELETE FROM telemeta_publishingstatus;
-INSERT INTO telemeta_publishingstatus (value)
- SELECT DISTINCT publishing_status FROM telemeta_collection
- WHERE publishing_status <> '' AND publishing_status IS NOT NULL;
+++ /dev/null
-#!/bin/bash
-
-if [ "$1" == "" ]
-then
- echo "Please provide the source directory"
- exit 1
-fi
-
-src=$1
-tmpdir=crem_import
-required="support.txt.gz phono.txt.gz"
-
-for f in $required
-do
- if ! [ -f $src/$f ]
- then
- echo "Can't find $f in $src"
- exit 1
- fi
-done
-
-if [ -d /tmp/$tmpdir ]
-then
- rm /tmp/$tmpdir/*
-else
- mkdir /tmp/$tmpdir
-fi
-
-echo -n "Converting charset and cleaning text files.. "
-
-zcat $src/support.txt.gz | tail -n +2 \
- | sed 's/^ *//' | sed 's/ *\t */\t/g'| sed 's/ *$//' \
- | iconv -f WINDOWS-1252 -c -t latin1 \
- > /tmp/$tmpdir/support.txt
-
-zcat $src/phono.txt.gz | tail -n +2 \
- | sed 's/^ *//' | sed 's/ *\t */\t/g'| sed 's/ *$//' \
- | iconv -f WINDOWS-1252 -c -t latin1 | sed '/^\t/d' \
- > /tmp/$tmpdir/phono.txt
-
-echo "Done"
-
-
+++ /dev/null
-#!/bin/bash
-
-python parse.py
-cd build
-rst2html docref.txt docref.html && rst2latex docref.txt docref.tex && pdflatex docref.tex
-
+++ /dev/null
-
-
-===================================================================
-Recommandations pour la réorganisation de la base de donnée du CREM
-===================================================================
-------------------------------------
-Document de référence, version alpha
-------------------------------------
-
-
-I - Enjeux
-==========
-
-I.1 - Recherche d'informations
-------------------------------
-
-L'analyse de la structure de données et la rencontre des personnes concernées par son usage
-permettent de délimiter des enjeux majeurs. Il apparaît notamment que le fond documentaire
-que constitue cette base n'est actuellement pas mis à profit comme il pourrait l'être. Cette
-base est largement sous-utilisée, les chercheurs, pour certains, ne l'utilisent pas du tout.
-
-La raison principale de cette situation est le fait que l'environnement technique et la
-structure de la base ont été conçus pour s'inscrire dans un environnement documentaire
-traditionnel, qui implique généralement de prendre contact avec un documentaliste pour effectuer
-une recherche. Le documentaliste est un spécialiste de la base, il possède les connaissances
-techniques nécessaires à sa consultation.
-
-Mais à l'heure d'Internet, ce processus de recherche est trop lourd et inadapté, en un mot
-: obsolète. Les chercheurs lui préfèrent d'autres fonds documentaires accessibles en ligne,
-de façon immédiate, et équipés d'outils de recherche puissants et ergonomiques.
-
-L'enjeu principal de la réorganisation de la base du CREM consiste donc à : sous-tendre le
-développement d'un outil moderne de gestion et de consultation en ligne, avec des fonctions
-de recherche puissantes et intuitives déterminer et améliorer les données de la base qui
-sont prioritaires pour la recherche d'information
-
-En de-ça des-dits champs prioritaires, il existe un grand nombre d'information techniques de
-second ordre, qui soit ne sont utiles qu'aux documentalistes, soit ne présentent pas d'intérêt
-majeur pour la recherche d'information. Ces informations peuvent être conservées telles quelles,
-sans grand effort de réorganisation, et resteront ainsi disponibles lors de la consultation
-individuelle des fiches.
-
-En procédant de cette façon, c'est à dire en délimitant les-dîtes priorités, il semble
-possible de dégager une véritable plus-value, de rendre le fond documentaire du CREM plus vivant,
-sans pour autant engager des moyens démesurés dans la réorganisation de la base de donnée.
-
-I.2 - Gestion et mise à jour des données
-----------------------------------------
-
-Il est entendu qu'au delà de la consultation d'information, il s'agit également de faciliter
-la gestion des données, leur mise à jour. Cette question implique pour une large part des
-problématiques d'ordre ergonomique, liées au développement d'un outil logiciel autour de la
-base, ce qui n'entre pas dans le cadre de la réorganisation de la base de données.
-
-Cependant, au niveau de la base, certains choix sont structurant. Par exemple l'emploi d'une
-liste hiérarchisée de lieux telle que le Thesaurus of Geographical Names (TGN, recommandé par
-Dublin Core), permet, en choisissant une région, une sous-région ou un village, de renseigner
-automatiquement le pays (et les autres zones géographiques intermédiaires), ce qui diminue
-l'effort et prévient les erreurs de saisie.
-
-Pour un grand nombre de champs, l'emploi d'énumérations (liste de valeurs valides pour
-un champ donné) facilite et valide déjà la mise à jour des données. Cet aspect de la base
-devra être conservé.
-
-Cependant, il semble que l'enjeu majeur de la réorganisation, du point de vue de la gestion
-des données, est de permettre un travail collaboratif, entre chercheurs et documentalistes. C'est
-là, d'une manière générale, ce qui permet sur Internet de rendre un fond documentaire vivant.
-
-Dans le cadre du CREM, il serait ainsi idéal d'amener les chercheurs à alimenter la base de
-façon autonome, en sus des documentalistes dont c'est le métier. Ce serait là une véritable
-nouveauté, représentant un fort potentiel d'enrichissement des données.
-
-D'une façon générale, une application en ligne ("webifiée") bien conçue se prête
-très bien au travail collaboratif. Cependant, il semble que dans notre cas, les potentielles
-contributions des chercheurs se heurtent à une question importante de confidentialité et
-de propriété intellectuelle. Des cas ont en effet été rapportés d'usage détournés du
-fruit du travail du centre de recherche, et d'une manière générale, il semble qu'il faille
-observer une grande prudence pour la mise en commun d'enregistrement sonores et autres données,
-qui représentent une matière précieuse aux yeux des chercheurs.
-
-La sécurité informatique est pour une large part distinct de la base de donnée ; elle
-implique des efforts particuliers au niveau du développement applicatif, et de l'administration
-de l'infrastructure réseau.
-
-Cependant, au niveau de la base de donnée, il est possible de mettre l'accent sur la
-propriété des données. Chaque ressource (collection, item) peut en effet se voir attribuer
-un propriétaire. Le dit propriétaire devrait ensuite pouvoir choisir quel autre utilisateur
-et/ou groupe d'utilisateur est autorisé à consulter, écouter, et/ou modifier la ressource.
-
-En rassurant ainsi les chercheurs, il est envisageable que la base s'enrichisse notablement
-par leurs contributions, dont certaines d'abord privées, pourront, à leur guise, devenir
-petit à petit accessibles à un nombre grandissant d'utilisateurs et de groupes de travail.
-
-I.3 - Données prioritaires
-
-Après consultation des différents utilisateurs il apparaît que les meta-données principales
-pour chaque item sont :
-
-* l'identifiant unique de l'item
-* le titre de l'item
-* la zone géographique
-* le nom du collecteur, c'est à dire le chercheur qui a collecté cet enregistrement
-* les instruments de musique utilisés
-* l'année d'enregistrement
-* le contexte ethnographique
-* l'ethnie
-
-Il s'agit d'apporter une attention particulière à celles-ci, notamment en les normalisant,
-pour faciliter la recherche d'informations, mais aussi pour l'ajout et la gestion simplifiés
-d'items.
-
-Les autres données pourront être conservées telles quelles sans grand effort de normalisation.
-
-I.4 - Dublin Core
------------------
-
-...
-
-II - Modalités de conversion/réorganisation de la base
-======================================================
-
-II.1 - Enumérations simples
----------------------------
-
-dynamic:tables.describe_enums()
-
-II.2 - Collections
-------------------
-
-Ancien nom de table : support
-
-Nouveau nom de table : media_collections
-
-II.2.1 - Champs convertis
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-dynamic:tables.media_collections.describe_conversion()
-
-II.2.2 - Nouveaux champs
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-dynamic:tables.media_collections.describe_new_fields()
-
-II.2.3 - Champs supprimés
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-dynamic:tables.media_collections.describe_removed_fields()
-
-II.3 - Items
-------------
-
-Ancien nom de table : phono
-
-Nouveau nom de table : media_items
-
-II.3.1 - Champs convertis
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-dynamic:tables.media_items.describe_conversion()
-
-II.3.2 - Nouveaux champs
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-dynamic:tables.media_items.describe_new_fields()
-
-II.3.3 - Champs supprimés
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-dynamic:tables.media_items.describe_removed_fields()
-
-II.4 - Sélections
------------------
-
-II.4.1 - Liste
-~~~~~~~~~~~~~~
-
-Nom de table:
-dynamic:tables.media_playlists.name
-
-dynamic:tables.media_playlists.describe_new_fields()
-
-II.4.2 - Table de relation interne
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Nom de table:
-dynamic:tables.media_playlist_resources.name
-
-dynamic:tables.media_playlist_resources.describe_new_fields()
-
-II.5 - Thesaurus Géographique
------------------------------
-
-II.5.1 - Lieux
-~~~~~~~~~~~~~~
-
-Nom de table:
-dynamic:tables.locations.name
-
-dynamic:tables.locations.describe_new_fields()
-
-II.5.2 - Alias des lieux
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Nom de table:
-dynamic:tables.location_aliases.name
-
-dynamic:tables.location_aliases.describe_new_fields()
-
-II.5.3 - Relations hiérarchiques
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Nom de table:
-dynamic:tables.location_relations.name
-
-dynamic:tables.location_relations.describe_new_fields()
-
-II.6 - Utilisateurs
--------------------
-
-Nom de table:
-dynamic:tables.users.name
-
-dynamic:tables.users.describe_new_fields()
+++ /dev/null
-#coding: utf-8
-import sqlalchemy
-import texttable
-
-def make_table(rows, width=20):
- return texttable.indent(rows, hasHeader = True, separateRows = True,
- prefix=u'| ', postfix=u' |',
- wrapfunc = lambda x: texttable.wrap_onspace(x, width=width))
-
-def describe_enums(metadata):
- rows = [['Nom interne', 'Ancien nom', 'Description']]
- for enum in metadata.enums:
- rows.append([enum.name, enum.old_name, enum.label])
- return make_table(rows, 40)
-
-class Column(sqlalchemy.Column):
- old_name = ''
- label = ''
- desc = ''
- dc = ''
- comment = ''
- conversion = ''
- enum = None
-
- def __init__(self, name, type, *args, **kwargs):
- parent_args = {}
- for key, value in kwargs.items():
- if key == 'old_name':
- if isinstance(value, list):
- self.old_name = unicode(",\n".join(value), 'utf-8')
- #self.old_name = []
- #for item in value:
- # self.old_name.append(unicode(item, 'utf-8'))
- else:
- self.old_name = unicode(value, 'utf-8')
- elif key == 'label':
- self.label = unicode(value, 'utf-8')
- elif key == 'desc':
- self.desc = unicode(value, 'utf-8')
- elif key == 'dc':
- self.dc = value
- elif key == 'comment':
- self.comment = unicode(value, 'utf-8')
- elif key == 'conversion':
- self.conversion = unicode(value, 'utf-8')
- else:
- parent_args[key] = value
-
- if isinstance(type, Enumeration):
- args = (sqlalchemy.ForeignKey(type.name + '.id'),) + args
- self.enum = type
- type = sqlalchemy.Integer
-
- super(Column, self).__init__(name, type, *args, **parent_args)
-
-class RemovedColumn(object):
- old_name = ''
- comment = ''
-
- def __init__(self, old_name, comment=''):
- self.old_name = unicode(old_name, 'utf-8')
- self.comment = unicode(comment, 'utf-8')
-
-class Table(sqlalchemy.Table):
-
- def __init__(self, table_name, metadata, label, *args):
- self.label = unicode(label, 'utf-8')
- real_columns = []
- self.removed_columns = []
- for column in args:
- if isinstance(column, RemovedColumn):
- self.removed_columns.append(column)
- else:
- real_columns.append(column)
- super(Table, self).__init__(table_name, metadata, *real_columns)
-
-
- def describe_new_fields(self):
- rows = [['Nom', 'Nom interne', 'Dublin Core']]
- for column in self.columns:
- if not column.old_name:
- rows.append([column.label, column.name, column.dc])
-
- return make_table(rows)
-
- def describe_conversion(self):
- rows = [['Nouveau nom', 'Ancien nom', 'Nouveau nom interne', 'Dublin Core']]
- for column in self.columns:
- if column.old_name:
- rows.append([column.label, column.old_name, column.name, column.dc])
-
- return make_table(rows)
-
- def describe_removed_fields(self):
- rows = [['Nom', 'Commentaire']]
- for column in self.removed_columns:
- rows.append([column.old_name, column.comment])
-
- return make_table(rows, 50)
-
- def to_dot(self):
- dot = u'digraph g {\n'
- dot += ' charset = "utf-8";\n'
- dot += ' node [shape=record, charset="utf-8"];\n'
- dot += ' rankdir = LR;\n'
- dot += ' subgraph cluster_new_fields {\n'
- dot += ' label = "Nouveaux champs";\n'
- dot += ' color = black;\n'
- old_fields = ''
- conversion = ''
- for column in self.columns:
- dot += ' ' + column.name + '[label = "{' + column.label + ' | ' + column.name + '}"];\n'
- if column.old_name:
- old_fields += ' old_' + column.name + '[label = "' + column.old_name + '"];\n'
- conversion += ' old_' + column.name + ' -> ' + column.name + ';\n'
- dot += ' }\n'
- dot += ' subgraph cluster_old_fields {\n'
- dot += ' label = "Anciens champs";\n'
- dot += ' color = black;\n'
- dot += old_fields
- dot += ' }\n'
- dot += conversion
- dot += '}\n'
- return dot
-
-class Enumeration(Table):
-
- def __init__(self, name, metadata, label='', old_name=''):
- self.old_name = unicode(old_name, 'utf-8')
- if not hasattr(metadata, 'enums'):
- metadata.enums = []
- metadata.enums.append(self)
- super(Enumeration, self).__init__(name, metadata, label,
- Column('id', sqlalchemy.Integer, primary_key=True),
- Column('value', sqlalchemy.String(250), unique=True)
- )
+++ /dev/null
-
-import tables
-import codecs
-input = codecs.open('docref.txt', 'r', "utf-8")
-output = codecs.open('build/docref.txt', 'w', "iso-8859-1")
-
-for line in input:
- if line[0:8] == 'dynamic:':
- output.write(eval(line[8:], globals(), locals()) + "\n")
- else:
- output.write(line)
-
-input.close()
-output.close()
-
-output = codecs.open('build/collections.dot', 'w', 'utf-8')
-output.write(tables.media_collections.to_dot())
-output.close()
+++ /dev/null
-#!python\r
-\r
-## The MIT License\r
-\r
-## Copyright (c) <year> <copyright holders>\r
-\r
-## Permission is hereby granted, free of charge, to any person obtaining a copy\r
-## of this software and associated documentation files (the "Software"), to deal\r
-## in the Software without restriction, including without limitation the rights\r
-## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-## copies of the Software, and to permit persons to whom the Software is\r
-## furnished to do so, subject to the following conditions:\r
-\r
-## The above copyright notice and this permission notice shall be included in\r
-## all copies or substantial portions of the Software.\r
-\r
-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
-## THE SOFTWARE.\r
-\r
-\r
-from sqlalchemy import types, exceptions\r
-\r
-class StaticEnum(types.TypeDecorator):\r
- impl = types.Unicode\r
- \r
- def __init__(self, values, empty_to_none=False, strict=False):\r
- """Emulate an Enum type.\r
-\r
- values:\r
- A list of valid values for this column\r
- empty_to_none:\r
- Optional, treat the empty string '' as None\r
- strict:\r
- Also insist that columns read from the database are in the\r
- list of valid values. Note that, with strict=True, you won't\r
- be able to clean out bad data from the database through your\r
- code.\r
- """\r
-\r
- if values is None or len(values) is 0:\r
- raise exceptions.AssertionError('StaticEnum requires a list of values')\r
- self.empty_to_none = empty_to_none\r
- self.strict = strict\r
- self.values = values[:]\r
-\r
- # The length of the string/unicode column should be the longest string\r
- # in values\r
- size = max([len(v) for v in values if v is not None])\r
- super(StaticEnum, self).__init__(size) \r
- \r
- \r
- def convert_bind_param(self, value, engine):\r
- if self.empty_to_none and value is '':\r
- value = None\r
- if value not in self.values:\r
- raise exceptions.AssertionError('"%s" not in StaticEnum.values' % value)\r
- return super(StaticEnum, self).convert_bind_param(value, engine)\r
- \r
- \r
- def convert_result_value(self, value, engine):\r
- if self.strict and value not in self.values:\r
- raise exceptions.AssertionError('"%s" not in StaticEnum.values' % value)\r
- return super(StaticEnum, self).convert_result_value(value, engine)\r
-\r
+++ /dev/null
-#coding: utf-8
-
-import sqlalchemy
-from sqlalchemy import String, Integer, ForeignKey, Time, Date, MetaData, Text, Boolean
-from elements import Column, RemovedColumn, Table, Enumeration
-import elements
-from staticenum import StaticEnum
-
-metadata = MetaData()
-def describe_enums():
- return elements.describe_enums(metadata)
-
-# Users
-
-users = Table('users', metadata, 'Utilisateurs',
- Column('username', String(250), primary_key=True, label='Nom d\'utilisateur'),
- Column('level', StaticEnum('user,maintainer,administrator'), label='Niveau de permissions'),
- Column('first_name', String(250), label='Prénom'),
- Column('last_name', String(250), label='Nom'),
- Column('phone', String(250), label='Téléphone'),
- Column('email', String(250), label='E-Mail')
-)
-
-# Playlists
-
-media_playlists = Table('playlists', metadata, 'Sélections personelles d\'items et de collections',
- Column('id', Integer, primary_key='true', label='Identifiant'),
- Column('owner_username', String(250), ForeignKey('users.username'), label='Propriétaire'),
- Column('name', String(250), label='Intitulé'))
-
-media_playlist_resources = Table('playlist_resources', metadata, 'Ressources associées aux sélections personelles ',
- Column('playlist_id', Integer, ForeignKey('playlists.id'), label='Identifiant de la sélection'),
- Column('resource_type', StaticEnum('item', 'collection'), label='Type de ressource (item, collection)'),
- Column('resource_id', String(250), label='Identifiant de la ressource')
-)
-
-# Simple enumerations
-
-physical_formats = Enumeration('physical_formats', metadata, 'Formats physiques', old_name='Format (e)')
-publishing_status = Enumeration('publishing_status', metadata, 'Status d\'édition/réédition', old_name='Réédition (e)')
-publishers = Enumeration('publishers', metadata, 'Editeurs', old_name='Editeur1 (e)')
-acquisition_modes = Enumeration('acquisition_modes', metadata, 'Modes d\'acquisition', old_name='Mode_Acqui (e)')
-metadata_authors = Enumeration('record_authors', metadata, 'Rédacteurs des fiches', old_name='Rédacteur_Fiche (e)')
-metadata_writers = Enumeration('metadata_writers', metadata, 'Opérateur de saisie des fiches', old_name='Saisie_Fiche (e)')
-legal_rights = Enumeration('legal_rights', metadata, 'Statuts juridiques', old_name='Droit_d\'Utiliser (e)')
-recording_contexts = Enumeration('recording_contexts', metadata, 'Contextes d\'enregistrement', old_name='Terrain_ou_Autre (e)')
-ad_conversions = Enumeration('ad_conversions', metadata, 'Statuts de numérisation', old_name='Numérisation (e)')
-ethnic_groups = Enumeration('ethnic_groups', metadata, 'Ethnies/Groupe social', old_name='Ethnie (t)')
-vernacular_styles = Enumeration('vernacular_styles', metadata, 'Forme / genre vernaculaire', old_name='Form (t)')
-generic_styles = Enumeration('generic_styles', metadata, 'Forme / genre générique', old_name='FormStyle générique (e)')
-context_keywords = Enumeration('context_keywords', metadata, 'Mots clés du contexte ethnographique', old_name='Mot_Clef (t)')
-publisher_collections = Enumeration('publisher_collections', metadata, 'Collections éditeur', old_name='Collection_Série (e)')
-
-# Geographic Thesaurus
-
-location_types = Enumeration('location_types', metadata, 'GeoEthno / Types de lieux')
-
-locations = Table('locations', metadata, 'GeoEthno / Lieux)',
- Column('name', String(127), primary_key=True, label='Terme descripteur'),
- Column('type', StaticEnum('country', 'continent', 'other')),
- Column('complete_type', location_types),
- Column('current_name', String(127), ForeignKey('locations.name'), label='Nom actuel'),
- Column('is_authoritative', Boolean),
-)
-
-location_aliases = Table('location_aliases', metadata, 'GeoEthno / Alias des lieux',
- Column('location_name', String(127), ForeignKey('locations.name'), primary_key=True),
- Column('alias', String(127), primary_key=True),
- Column('is_authoritative', Boolean))
-
-location_relations = Table('location_relations', metadata, 'GeoEthno / Relations hiérachiques',
- Column('location_name', String(127), ForeignKey('locations.name'), primary_key=True),
- Column('parent_location_name', String(127), ForeignKey('locations.name'), primary_key=True)
-)
-
-# Media Collections
-
-media_collections = Table('media_collections', metadata, 'Collections',
- Column('reference',
- String(250), unique=True,
- old_name='Réf', label='Référence'),
- Column('physical_format_id',
- physical_formats,
- old_name='Format', label='Format',
- desc="Format du 1er exemplaire archivé"),
- Column('old_id',
- String(250),
- old_name='Cote', label='Ancienne cote'),
- Column('id',
- String(250), primary_key=True,
- dc='identifier',
- label='Cote', conversion='à préciser'),
- Column('title',
- String(250),
- dc='title',
- old_name='Titre', label='Titre'),
- Column('native_title',
- String(250),
- dc='title',
- old_name='Transcrip_Trad', label='Traduction du titre'),
- Column('physical_items_num',
- Integer,
- old_name='Nb_de_pieces', label='Nombre de supports physiques'),
- Column('publishing_status_id',
- publishing_status,
- old_name='Réédition', label='Réédition'),
- RemovedColumn(old_name='Original'),
- RemovedColumn(old_name='Copie_TotPartie'),
- RemovedColumn(old_name='Copié_de'),
- Column('creator',
- String(250),
- dc='creator',
- old_name='Auteur_Compil', label='Auteur / Cédant'),
- Column('booklet_author',
- String(250),
- dc='contributor',
- old_name='Auteur_Notice', label='Auteur notice'),
- Column('booklet_description',
- Text,
- old_name='Notice', label='Notice / Dossier technique'),
- Column('collector',
- Text,
- dc='contributor',
- old_name='Collecteur', label='Collecteur'),
- Column('publisher_id',
- publishers,
- dc='publisher',
- old_name='Editeur', label='Editeur',
- desc='Pour les documents ÉDITÉS:Nom et État de l\'Editeur. Pour les INÉDITS :voir champ "Type de document".'),
- Column('year_published',
- Integer,
- dc='date',
- old_name='Année_parution', label='Année de parution',
- desc='Ne concerne que les documents ÉDITÉS.',
- conversion='à préciser, traiter les nombres négatifs ?'),
- Column('publisher_collection',
- publisher_collections,
- old_name='Collect_Série', label='Collection éditeur',
- comment='faux: nom de la collection, suivi du n° dans la collection.'),
- Column('publisher_serial',
- String(250),
- old_name='Num_Dans_Collec', label='Numéro de série',
- desc='Numéro de série dans la collection éditeur',
- comment='à valider'),
- Column('external_references',
- Text,
- old_name='Réf_Biblio', label='Bibliographie',
- desc='Références biblio/disco/filmographiques, uniquement liées à ce support'),
- Column('acquisition_mode_id',
- acquisition_modes,
- old_name='Mod_Acqui', label='Mode d\'acquisition'),
- Column('comment',
- Text,
- old_name='Commentaire', label='Commentaire'),
- Column('metadata_author_id',
- metadata_authors,
- dc='contributor',
- old_name='Rédacteur_Fiche', label='Rédacteur fiche',
- desc='Responsable de l\'analyse documentaire'),
- Column('metadata_writer_id',
- metadata_writers,
- old_name='Saisie_Fiche', label='Saisie fiches',
- desc='Personne qui a saisi les fiches dans la base de données.'),
- Column('legal_rights_id',
- legal_rights,
- dc='rights',
- old_name='Droit_Utiliser', label='Statut juridique'),
- Column('alt_ids',
- String(250),
- old_name='Autres_Cotes', label='Autres exemplaires'),
- Column('recorded_from_year',
- Integer,
- dc='date',
- old_name='Année_Enreg', label='Années d\'enregistrement',
- conversion="split"),
- Column('recorded_to_year',
- Integer,
- dc='date',
- old_name='Année_Enreg', label='Années d\'enregistrement',
- conversion="split"),
- Column('recording_context_id',
- recording_contexts,
- old_name='Terrain_ou_Autr', label='Contexte d\'enregistrement'),
- Column('approx_duration',
- Time(),
- old_name='Durée_approx', label='Durée approximative'),
- Column('doctype_code',
- Integer,
- old_name='Tri_DiBm', label='Type de document'),
- Column('travail',
- String(250),
- old_name='Travail', label='?'),
- Column('state',
- String(250),
- old_name='Compil_Face_Plage', label="Etat"),
- Column('cnrs_contributor',
- String(250),
- old_name='Déposant_CNRS', label="Déposant CNRS",
- desc='Pour les INÉDITS uniquement. Signale les collectes ayant bénéficées d\'une aide du CNRS.'),
- Column('items_done',
- String(250),
- old_name='Fiches', label='Fiches faîtes',
- desc="Signale que les fiches Item de ce support sont présentes dans la base."),
- Column('a_informer_07_03',
- String(250),
- old_name='A informer_07-03', label='?'),
- Column('ad_conversion_id',
- ad_conversions,
- old_name='Numérisation', label="Historique de numérisation"),
- RemovedColumn(old_name='Champ36'),
-)
-
-# Media Items
-
-media_item_context_keywords = Table('media_item_context_keywords', metadata,
- 'Mots clés associés à un item',
- Column('media_item_id', Integer, ForeignKey('media_items.id')),
- Column('context_keyword_id', context_keywords))
-
-media_items = Table('media_items', metadata, 'Items',
- RemovedColumn(old_name='Réf', comment='Calculé en temps réel à partir de la collection'),
- Column('collection_id',
- String(250), ForeignKey('media_collections.id'),
- dc='relation/isPartOf',
- label='Collection'),
- RemovedColumn(old_name='Format', comment='Calculé en temps réel à partir de la collection'),
- RemovedColumn(old_name='Cote_Support', comment='Calculé en temps réel à partir de la collection'),
- Column('track',
- String(250),
- old_name="Face_Plage", label="N° de l'item"),
- Column('id',
- String(250),
- old_name='Cote_Phono', label="Cote item"),
- Column('approx_duration',
- Time(),
- old_name='Durée', label='Durée'),
- Column('recorded_from_date',
- Date(),
- dc='date',
- old_name='Date_enregistr', label='Date', desc='Date de l\'enregistrement'),
- Column('recorded_to_date',
- Date(),
- dc='date',
- old_name='Date_enregistr', label='Date'),
- Column('location_name',
- Integer, ForeignKey('locations.name'),
- dc='coverage',
- old_name=['Continent', 'Etat', 'Région_Village'], label='Lieu',
- desc='(?) Lieu de provenance de la musique. Si le lieu de l\'enregistrement est autre, l\'indiquer en "Remarques".'),
- Column('location_comment',
- String(250),
- old_name='Région_Village', label='Commentaire lieu'),
- Column('ethnic_group_id',
- ethnic_groups,
- old_name='Ethnie_GrSocial', label='Ethnie/Groupe social', comment='attention alias ethnies'),
- RemovedColumn(old_name='Titre_Support', comment='Calculé en temps réel à partir de la collection'),
- Column('title',
- String(250),
- dc='title',
- old_name='Titre_pièce', label='Titre Item'),
- Column('native_title',
- String(250),
- dc='title',
- old_name='Transcrip_Trad', label='Traduction du titre',
- desc='Traduction des langues non communes, plus translittération des langues n\'utilisant pas l\'alphabet latin.'),
- Column('author',
- String(250),
- dc='creator',
- old_name='Auteur', label='Auteur',
- desc='Le cas échéant, faire suivre le nom par une mention "-auteur /-compositeur / -arrangeur"'),
- Column('vernacular_style_id',
- vernacular_styles,
- old_name='Form_Genr_Style', label='Forme / genre vernaculaire [nom à revoir]',
- desc='Nom local de la forme ou du genre'),
- RemovedColumn(old_name='Struct_Modale'),
- RemovedColumn(old_name='Struct_Rythm'),
- RemovedColumn(old_name='Struct_Rythm'),
- RemovedColumn(old_name='Fonction_Usage',
- comment='Champ inutile, les mots clés sont associés via une table de relation externe'),
- Column('context_comment',
- Text,
- old_name='Comm_FonctUsage', label='Contexte ethnographique : commentaires'),
- Column('external_references',
- String(250),
- old_name='Documentation', label='Références',
- desc='Références directement liées à l\'item.'),
- Column('moda_execut',
- String(250),
- old_name='Moda_Execut', comment='à supprimer ?'),
- Column('copied_from_item_id',
- String(250), ForeignKey('media_items.id'),
- dc='relation/isVersionOf',
- old_name='Copie_de', label='Copie de'),
- Column('collector',
- String(250),
- dc='contributor',
- old_name='Enregistré_par', label='Collecteur'),
- Column('cultural_area',
- String(250),
- old_name='Aire culturelle', label='Aire culturelle'),
- RemovedColumn(old_name='Année_Enreg',
- comment='calculé en temps-réel à partir de la date d\'enregistrement'),
- Column('generic_style',
- generic_styles,
- old_name='FormStyl généri', label='Forme / genre générique'),
- Column('collector_selection',
- String(250),
- old_name='ChoixCollecteur', label='Choix du collecteur',
- desc='Permet au collecteur de repérer les items les plus intéressants'),
- RemovedColumn(old_name='Repère_bande'),
- Column('creator_reference',
- String(250),
- old_name='NroBandNroPièc', label='Référence du déposant'),
-)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+++ /dev/null
-#coding: utf-8
-import cStringIO,operator
-
-def indent(rows, hasHeader=False, headerChar=u'-', delim=u' | ', justify=u'left',
- separateRows=False, prefix=u'', postfix=u'', wrapfunc=lambda x:x):
- """Indents a table by column.
- - rows: A sequence of sequences of items, one sequence per row.
- - hasHeader: True if the first row consists of the columns' names.
- - headerChar: Character to be used for the row separator line
- (if hasHeader==True or separateRows==True).
- - delim: The column delimiter.
- - justify: Determines how are data justified in their column.
- Valid values are 'left','right' and 'center'.
- - separateRows: True if rows are to be separated by a line
- of 'headerChar's.
- - prefix: A string prepended to each printed row.
- - postfix: A string appended to each printed row.
- - wrapfunc: A function f(text) for wrapping text; each element in
- the table is first wrapped by this function."""
- # closure for breaking logical rows to physical, using wrapfunc
- def rowWrapper(row):
- newRows = [wrapfunc(item).split('\n') for item in row]
- return [[substr or '' for substr in item] for item in map(None,*newRows)]
- # break each logical row into one or more physical ones
- logicalRows = [rowWrapper(row) for row in rows]
- # columns of physical rows
- columns = map(None,*reduce(operator.add,logicalRows))
- # get the maximum of each column by the string length of its items
- maxWidths = [max([len(item) for item in column]) for column in columns]
- rowSeparator = u"+" + u"+".join([u'-' * (width + len(delim) - 1) for width in maxWidths]) + u"+"
- headerSep = u"+" + u"+".join([u'=' * (width + len(delim) - 1) for width in maxWidths]) + u"+"
- #headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
- # len(delim)*(len(maxWidths)-1))
- #headerSep = u"=" * (len(prefix) + len(postfix) + sum(maxWidths) + \
- # len(delim)*(len(maxWidths)-1))
- # select the appropriate justify method
- #justify = {'center':str.center, 'right':str.rjust, 'left':str.ljust}[justify.lower()]
- output=u''
- if separateRows:
- output += rowSeparator + "\n"
-
- for physicalRows in logicalRows:
- for row in physicalRows:
- output += \
- prefix \
- + delim.join([item.ljust(width) for (item,width) in zip(row,maxWidths)]) \
- + postfix + "\n"
- if hasHeader: output += headerSep + "\n"; hasHeader=False
- elif separateRows: output += rowSeparator + "\n"
- return output
-
-# written by Mike Brown
-# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
-def wrap_onspace(text, width):
- """
- A word-wrap function that preserves existing line breaks
- and most spaces in the text. Expects that existing line
- breaks are posix newlines (\n).
- """
- return reduce(lambda line, word, width=width: '%s%s%s' %
- (line,
- ' \n'[(len(line[line.rfind('\n')+1:])
- + len(word.split('\n',1)[0]
- ) >= width)],
- word),
- text.split(' ')
- )
-
-import re
-def wrap_onspace_strict(text, width):
- """Similar to wrap_onspace, but enforces the width constraint:
- words longer than width are split."""
- wordRegex = re.compile(r'\S{'+str(width)+r',}')
- return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width)
-
-import math
-def wrap_always(text, width):
- """A simple word-wrap function that wraps text on exactly width characters.
- It doesn't split the text in words."""
- return '\n'.join([ text[width*i:width*(i+1)] \
- for i in xrange(int(math.ceil(1.*len(text)/width))) ])
-
-if __name__ == '__main__':
- labels = ('First Name', 'Last Name', 'Age', 'Position')
- data = \
- '''John,Smith,24,Software Engineer
- Mary,Brohowski,23,Sales Manager
- Aristidis,Papageorgopoulos,28,Senior Reseacher'''
- rows = [row.strip().split(',') for row in data.splitlines()]
-
- print 'Without wrapping function\n'
- print indent([labels]+rows, hasHeader=True)
- # test indent with different wrapping functions
- width = 10
- for wrapper in (wrap_always,wrap_onspace,wrap_onspace_strict):
- print 'Wrapping function: %s(x,width=%d)\n' % (wrapper.__name__,width)
- print indent([labels]+rows, hasHeader=True, separateRows=True,
- prefix='| ', postfix=' |',
- wrapfunc=lambda x: wrapper(x,width))
-
- # output:
- #
- #Without wrapping function
- #
- #First Name | Last Name | Age | Position
- #-------------------------------------------------------
- #John | Smith | 24 | Software Engineer
- #Mary | Brohowski | 23 | Sales Manager
- #Aristidis | Papageorgopoulos | 28 | Senior Reseacher
- #
- #Wrapping function: wrap_always(x,width=10)
- #
- #----------------------------------------------
- #| First Name | Last Name | Age | Position |
- #----------------------------------------------
- #| John | Smith | 24 | Software E |
- #| | | | ngineer |
- #----------------------------------------------
- #| Mary | Brohowski | 23 | Sales Mana |
- #| | | | ger |
- #----------------------------------------------
- #| Aristidis | Papageorgo | 28 | Senior Res |
- #| | poulos | | eacher |
- #----------------------------------------------
- #
- #Wrapping function: wrap_onspace(x,width=10)
- #
- #---------------------------------------------------
- #| First Name | Last Name | Age | Position |
- #---------------------------------------------------
- #| John | Smith | 24 | Software |
- #| | | | Engineer |
- #---------------------------------------------------
- #| Mary | Brohowski | 23 | Sales |
- #| | | | Manager |
- #---------------------------------------------------
- #| Aristidis | Papageorgopoulos | 28 | Senior |
- #| | | | Reseacher |
- #---------------------------------------------------
- #
- #Wrapping function: wrap_onspace_strict(x,width=10)
- #
- #---------------------------------------------
- #| First Name | Last Name | Age | Position |
- #---------------------------------------------
- #| John | Smith | 24 | Software |
- #| | | | Engineer |
- #---------------------------------------------
- #| Mary | Brohowski | 23 | Sales |
- #| | | | Manager |
- #---------------------------------------------
- #| Aristidis | Papageorgo | 28 | Senior |
- #| | poulos | | Reseacher |
- #---------------------------------------------
-