diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..c9f9b0f --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,4 @@ +0.1 +--- + +- Initial version. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..7f786d8 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include cao_osint *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a83efd3 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# README # + +**cao_osint** est une application pour créer un blog simple. Elle est inspirée du tutorial [**pyramid_blogr**](https://docs.pylonsproject.org/projects/pyramid-blogr/en/latest/index.html). + +## Fonctionnalités ## + +Bien que **cao_osint** soit une application minimale et simple, elle possède toutes les fonctions nécessaire pour gérer un blog : + +- Gestion des utilisateurs du blog (admin ou rédacteur) +- Authentification et autorisation des utilisateurs +- Gestion des posts du blog avec des statuts : *publié, brouillon ou privé* +- Gestion des tags (un post peut avoir un tag) +- Les posts sont rédigé avec le langage *Markdown* +- Possibilité de faire des recherches sur le titre et le corps des posts + +## Add-on requis ## + +- Python 3.7.1 +- Pyramid 1.10 +- SQLite 3.35.5 +- pyramid-jinja2 2.7 : view templating +- wtforms 2.2.1 : form library +- webhelpers2 2.0 : various web building related helpers +- Markdown 3.4.1 : + + +### 1.0 (22.01.2023) + +- version initiale \ No newline at end of file diff --git a/cao_osint.sqlite b/cao_osint.sqlite new file mode 100644 index 0000000..ec58555 Binary files /dev/null and b/cao_osint.sqlite differ diff --git a/cao_osint/__init__.py b/cao_osint/__init__.py new file mode 100644 index 0000000..fc607aa --- /dev/null +++ b/cao_osint/__init__.py @@ -0,0 +1,27 @@ +from pyramid.config import Configurator +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy +from pyramid.session import SignedCookieSessionFactory + +from .services.user import groupfinder + + +def main(global_config, **settings): + """ This function returns a Pyramid WSGI application. + """ + # session factory + my_session_factory = SignedCookieSessionFactory('dnAFXEqr5VpKtW') + + authentication_policy = AuthTktAuthenticationPolicy('ssLZLBIN39jFWn', + callback=groupfinder, hashalg='sha512', timeout=36000) + authorization_policy = ACLAuthorizationPolicy() + with Configurator(settings=settings, + root_factory='cao_osint.security.RootFactory', + authentication_policy=authentication_policy, + authorization_policy=authorization_policy) as config: + config.include('pyramid_jinja2') + config.include('.models') + config.include('.routes') + config.set_session_factory(my_session_factory) + config.scan() + return config.make_wsgi_app() diff --git a/cao_osint/alembic/env.py b/cao_osint/alembic/env.py new file mode 100644 index 0000000..2be0ba6 --- /dev/null +++ b/cao_osint/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from cao_osint.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/cao_osint/alembic/script.py.mako b/cao_osint/alembic/script.py.mako new file mode 100644 index 0000000..535780d --- /dev/null +++ b/cao_osint/alembic/script.py.mako @@ -0,0 +1,22 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + +def upgrade(): + ${upgrades if upgrades else "pass"} + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/cao_osint/alembic/versions/20230121_a632e375e7dc.py b/cao_osint/alembic/versions/20230121_a632e375e7dc.py new file mode 100644 index 0000000..4092575 --- /dev/null +++ b/cao_osint/alembic/versions/20230121_a632e375e7dc.py @@ -0,0 +1,26 @@ +"""init + +Revision ID: a632e375e7dc +Revises: +Create Date: 2023-01-21 11:25:48.517435 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a632e375e7dc' +down_revision = None +branch_labels = None +depends_on = None + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('entries', 'author') + # ### end Alembic commands ### + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('entries', sa.Column('author', sa.VARCHAR(length=50), nullable=True)) + # ### end Alembic commands ### diff --git a/cao_osint/alembic/versions/README.txt b/cao_osint/alembic/versions/README.txt new file mode 100644 index 0000000..b0d704d --- /dev/null +++ b/cao_osint/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions diff --git a/cao_osint/forms.py b/cao_osint/forms.py new file mode 100644 index 0000000..304093d --- /dev/null +++ b/cao_osint/forms.py @@ -0,0 +1,36 @@ +from wtforms import Form, StringField, TextAreaField, SelectField, validators +from wtforms import IntegerField, PasswordField +from wtforms.validators import InputRequired, Length, EqualTo +from wtforms.widgets import HiddenInput + +strip_filter = lambda x: x.strip() if x else None + +class BlogCreateForm(Form): + title = StringField('Titre', validators=[InputRequired(), Length(min=1, max=255)], + filters=[strip_filter]) + body = TextAreaField('Corps du texte', validators=[InputRequired(), Length(min=1)], + filters=[strip_filter]) + tag = SelectField('Tag') + status = SelectField('Statut', choices=[('brouillon','Brouillon'),('privé','Privé'),('publié','Publié')]) + +class BlogUpdateForm(BlogCreateForm): + id = IntegerField(widget=HiddenInput()) + +class BlogSearchForm(Form): + criteria = StringField('Recherche', validators=[InputRequired(), Length(min=3, max=45)], + filters=[strip_filter]) + +class TagForm(Form): + id = IntegerField(widget=HiddenInput()) + tag = StringField('Tag', validators=[InputRequired(), Length(min=1, max=25)], + filters=[strip_filter]) + + +class UserCreateForm(Form): + id = IntegerField(widget=HiddenInput()) + name = StringField('Nom', validators=[InputRequired(), Length(min=1, max=255)], + filters=[strip_filter]) + password = PasswordField('Mot de passe') + + confirm = PasswordField('Confirmer', validators=[EqualTo('password', message='Les 2 mots de passe doivent être identiques')]) + diff --git a/cao_osint/models/__init__.py b/cao_osint/models/__init__.py new file mode 100644 index 0000000..5181e5a --- /dev/null +++ b/cao_osint/models/__init__.py @@ -0,0 +1,78 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import configure_mappers +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines +from .user import User +from .blog_record import BlogRecord + +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup +configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('cao_osint.models')``. + + """ + settings = config.get_settings() + settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + # use pyramid_retry to retry a request when transient exceptions occur + config.include('pyramid_retry') + + session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/cao_osint/models/blog_record.py b/cao_osint/models/blog_record.py new file mode 100644 index 0000000..6599fd6 --- /dev/null +++ b/cao_osint/models/blog_record.py @@ -0,0 +1,40 @@ +import datetime #<- will be used to set default dates on models +from cao_osint.models.meta import Base #<- we need to import our sqlalchemy metadata from which model classes will inherit +from sqlalchemy import ( + Column, + Integer, + Unicode, #<- will provide Unicode field + UnicodeText, #<- will provide Unicode text field + DateTime, #<- time abstraction field +) +from webhelpers2.text import urlify #<- will generate slugs +from webhelpers2.date import distance_of_time_in_words #<- human friendly dates + + +class BlogRecord(Base): + __tablename__ = 'entries' + id = Column(Integer, primary_key=True) + title = Column(Unicode(255), unique=True, nullable=False) + body = Column(UnicodeText, default='') + created = Column(DateTime, default=datetime.datetime.now) + creator = Column(Unicode(50), default='') + edited = Column(DateTime, default=datetime.datetime.now) + editor = Column(Unicode(50), default='') + tag = Column(Unicode(25)) + status = Column(Unicode(50), default='brouillon') + + @property + def slug(self): + return urlify(self.title) + + @property + def created_in_words(self): + return distance_of_time_in_words(self.created, + datetime.datetime.utcnow()) + + +class Tags(Base): + __tablename__ = 'tags' + id = Column(Integer, primary_key=True) + tag = Column(Unicode(25)) + diff --git a/cao_osint/models/meta.py b/cao_osint/models/meta.py new file mode 100644 index 0000000..02285b3 --- /dev/null +++ b/cao_osint/models/meta.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.schema import MetaData + +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.zzzcomputing.com/en/latest/naming.html +NAMING_CONVENTION = { + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(table_name)s_%(column_0_name)s", + "ck": "ck_%(table_name)s_%(constraint_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s" +} + +metadata = MetaData(naming_convention=NAMING_CONVENTION) +Base = declarative_base(metadata=metadata) diff --git a/cao_osint/models/user.py b/cao_osint/models/user.py new file mode 100644 index 0000000..595702c --- /dev/null +++ b/cao_osint/models/user.py @@ -0,0 +1,34 @@ +import datetime #<- will be used to set default dates on models +from cao_osint.models.meta import Base #<- we need to import our sqlalchemy metadata from which model classes will inherit +from sqlalchemy import ( + Column, + Integer, + Unicode, #<- will provide Unicode field + UnicodeText, #<- will provide Unicode text field + DateTime, #<- time abstraction field +) + +from passlib.apps import custom_app_context as blogger_pwd_context + + +class User(Base): + __tablename__ = 'users' + id = Column(Integer, primary_key=True) + name = Column(Unicode(255), unique=True, nullable=False) + password = Column(Unicode(255), nullable=False) + last_logged = Column(DateTime, default=datetime.datetime.utcnow) + + def verify_password(self, password): + # is it cleartext? + if password == self.password: + self.set_password(password) + # verify password + result = blogger_pwd_context.verify(password, self.password) + if result: + # pwd OK, set last login date + self.last_logged = datetime.datetime.now() + return result + + def set_password(self, password): + password_hash = blogger_pwd_context.encrypt(password) + self.password = password_hash diff --git a/cao_osint/pshell.py b/cao_osint/pshell.py new file mode 100644 index 0000000..b0847ee --- /dev/null +++ b/cao_osint/pshell.py @@ -0,0 +1,13 @@ +from . import models + + +def setup(env): + request = env['request'] + + # start a transaction + request.tm.begin() + + # inject some vars into the shell builtins + env['tm'] = request.tm + env['dbsession'] = request.dbsession + env['models'] = models diff --git a/cao_osint/routes.py b/cao_osint/routes.py new file mode 100644 index 0000000..9952818 --- /dev/null +++ b/cao_osint/routes.py @@ -0,0 +1,15 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') + config.add_route('apropos', '/apropos') + config.add_route('blog', '/blog/{id:\d+}/{slug}') + config.add_route('blog_edit', '/blog_edit/{id}') + config.add_route('blog_search', '/blog_search') + config.add_route('login', '/login') + config.add_route('logout', '/logout') + config.add_route('tags', '/tags') + config.add_route('tag_edit', '/tag_edit/{id}') + config.add_route('uploads', '/uploads') + config.add_route('upload_edit', '/upload_edit/{filename}') + config.add_route('users', '/users') + config.add_route('user_edit', '/user_edit/{name}') diff --git a/cao_osint/scripts/__init__.py b/cao_osint/scripts/__init__.py new file mode 100644 index 0000000..5bb534f --- /dev/null +++ b/cao_osint/scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/cao_osint/scripts/initialize_db.py b/cao_osint/scripts/initialize_db.py new file mode 100644 index 0000000..2820508 --- /dev/null +++ b/cao_osint/scripts/initialize_db.py @@ -0,0 +1,49 @@ +import argparse +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + """ + Add or update models / fixtures in the database. + + """ + + model = models.user.User(name=u'admin', password=u'pcao.7513') + dbsession.add(model) + + +def parse_args(argv): + parser = argparse.ArgumentParser() + parser.add_argument( + 'config_uri', + help='Configuration file, e.g., development.ini', + ) + return parser.parse_args(argv[1:]) + + +def main(argv=sys.argv): + args = parse_args(argv) + setup_logging(args.config_uri) + env = bootstrap(args.config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/cao_osint/security.py b/cao_osint/security.py new file mode 100644 index 0000000..c3c2ab1 --- /dev/null +++ b/cao_osint/security.py @@ -0,0 +1,15 @@ +from pyramid.security import ( + Allow, + Authenticated, + DENY_ALL, + ) + +class RootFactory(object): + """Defines an ACL for groups/permissions mapping""" + __acl__ = [ (Allow, Authenticated, 'view'), + (Allow, 'group:administrators', 'manage'), + DENY_ALL, + ] + def __init__(self, request): + pass + diff --git a/cao_osint/services/__init__.py b/cao_osint/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cao_osint/services/blog_record.py b/cao_osint/services/blog_record.py new file mode 100644 index 0000000..46283f2 --- /dev/null +++ b/cao_osint/services/blog_record.py @@ -0,0 +1,57 @@ +import sqlalchemy as sa +import datetime #<- will be used to set default dates on models + +from sqlalchemy import or_ +from ..models.blog_record import BlogRecord, Tags + + + +class BlogRecordService(object): + + @classmethod + def by_criteria(cls, request, criteria): + search = "%{}%".format(criteria) + query = request.dbsession.query(BlogRecord) + if request.authenticated_userid == None: + # if user is anonym, display only published posts + query = query.filter(BlogRecord.status == 'publié') + query = query.filter(or_(BlogRecord.title.like(search), + BlogRecord.body.like(search))).all() + return query + + @classmethod + def by_id(cls, request, _id): + query = request.dbsession.query(BlogRecord) + return query.get(_id) + + @classmethod + def get_last_created(cls, request): + # gest the 10 last created posts + query = request.dbsession.query(BlogRecord) + if request.authenticated_userid == None: + # if user is anonym, display only published posts + query = query.filter(BlogRecord.status == 'publié') + query = query.order_by(sa.desc(BlogRecord.created)).limit(10).all() + return query + + @classmethod + def delete(cls, request, id): + request.dbsession.query(BlogRecord).filter(BlogRecord.id == id).delete(synchronize_session=False) + return + + @classmethod + def get_tags(cls, request): + query = request.dbsession.query(Tags) + query = query.order_by(Tags.tag).all() + return query + + @classmethod + def get_tags_byId(cls, request, id): + # gest the last 5 items modified + query = request.dbsession.query(Tags).filter(Tags.id == id).first() + return query + + @classmethod + def tag_delete(cls, request, id): + request.dbsession.query(Tags).filter(Tags.id == id).delete(synchronize_session=False) + return diff --git a/cao_osint/services/user.py b/cao_osint/services/user.py new file mode 100644 index 0000000..2d771f1 --- /dev/null +++ b/cao_osint/services/user.py @@ -0,0 +1,32 @@ +import sqlalchemy as sa +from ..models.user import User + + +class UserService(object): + + @classmethod + def all(cls, request): + items = request.dbsession.query(User).order_by(sa.asc(User.name)).all() + return items + + @classmethod + def by_name(cls, request, name ): + item = request.dbsession.query(User).filter(User.name == name).first() + return item + + @classmethod + def delete(cls, request, id): + request.dbsession.query(User).filter(User.id == id).delete(synchronize_session=False) + return + +def groupfinder(userid, request): + + if userid: + # user name is 'admin' ? + if userid == 'admin': + return ['group:administrators'] + else: + return [] # it means that userid is logged in + else: + # it returns None if userid isn't logged in + return None diff --git a/cao_osint/static/favicon.ico b/cao_osint/static/favicon.ico new file mode 100644 index 0000000..1f2210f Binary files /dev/null and b/cao_osint/static/favicon.ico differ diff --git a/cao_osint/static/pyramid-16x16.png b/cao_osint/static/pyramid-16x16.png new file mode 100644 index 0000000..9792031 Binary files /dev/null and b/cao_osint/static/pyramid-16x16.png differ diff --git a/cao_osint/static/pyramid.png b/cao_osint/static/pyramid.png new file mode 100644 index 0000000..4ab837b Binary files /dev/null and b/cao_osint/static/pyramid.png differ diff --git a/cao_osint/static/theme.css b/cao_osint/static/theme.css new file mode 100644 index 0000000..302815a --- /dev/null +++ b/cao_osint/static/theme.css @@ -0,0 +1,143 @@ +/* Theme inspired from Bootstrap Theme "The Band" */ +/* https://www.w3schools.com/bootstrap/bootstrap_theme_band.asp */ + +body { + font: 400 19px/1.8 Georgia, serif; + color: #777; +} +h3, h4 { + margin: 10px 0 30px 0; + letter-spacing: 10px; + font-size: 20px; + color: #111; +} +.container { + padding: 80px 120px; +} +.person { + border: 10px solid transparent; + margin-bottom: 25px; + width: 80%; + height: 80%; + opacity: 0.7; +} +.person:hover { + border-color: #f1f1f1; +} +.carousel-inner img { + -webkit-filter: grayscale(90%); + filter: grayscale(90%); /* make all photos black and white */ + width: 100%; /* Set width to 100% */ + margin: auto; +} +.carousel-caption h3 { + color: #fff !important; +} +@media (max-width: 600px) { + .carousel-caption { + display: none; /* Hide the carousel text when the screen is less than 600 pixels wide */ + } +} +.bg-1 { + background: #bc2131; + color: #bdbdbd; +} +.bg-1 h3 {color: #fff;} +.bg-1 p {font-style: italic;} +.list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.list-group-item:last-child { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.thumbnail { + padding: 0 0 15px 0; + border: none; + border-radius: 0; +} +.thumbnail p { + margin-top: 15px; + color: #555; +} + +.modal-header, h4, .close { + background-color: #333; + color: #fff !important; + text-align: center; + font-size: 30px; +} +.modal-header, .modal-body { + padding: 40px 50px; +} +.nav-tabs li a { + color: #777; +} +#googleMap { + width: 100%; + height: 400px; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); +} +.navbar { + font-family: Montserrat, sans-serif; + margin-bottom: 0; + background-color: #bc2131; + border: 0; + font-size: 14px !important; + letter-spacing: 4px; + opacity: 0.9; +} +.navbar li a, .navbar .navbar-brand { + color: #ffffff !important; +} +.navbar-nav li a:hover { + color: #fff !important; +} +.navbar-nav li.active a { + color: #fff !important; + background-color: #29292c !important; +} +.navbar-default .navbar-toggle { + border-color: transparent; +} +footer { + background-color: #bc2131; + color: #f5f5f5; + padding: 32px; +} +footer a { + color: #f5f5f5; +} +footer a:hover { + color: #777; + text-decoration: none; +} +.form-control { + border-radius: 0; +} +textarea { + resize: none; +} +.required-field::after { + content: "*"; + color: red; + margin-left:2px; +} + +/* Dropdown */ +.open .dropdown-toggle { + color: #fff ; + background-color: #555 !important; +} + +/* Dropdown links */ +.dropdown-menu li a { + color: #000 !important; +} + +/* On hover, the dropdown links will turn red */ +.dropdown-menu li a:hover { + background-color: red !important; +} \ No newline at end of file diff --git a/cao_osint/static/uploads/articles.csv b/cao_osint/static/uploads/articles.csv new file mode 100644 index 0000000..c9ab067 --- /dev/null +++ b/cao_osint/static/uploads/articles.csv @@ -0,0 +1,253 @@ +REF;LIBART;libelle;PRIXHT1;PRIXHT2;PRIXHT3;PRIXHT4;PRIXHT5;PRIXHT6;PRIXHT7;PRIXHT8;PRIXHT9;PRIXHT10;ref_cli1;ref_cli2;ref_cli3;ref_cli4;ref_cli5;ref_cli6;ref_cli7;ref_cli8;ref_cli9;ref_cli10;UNITE;FAM;LIBCOMPL1;LIBCOMPL2;LIBCOMPL3;LIBCOMPL4;cree_le;modif_le +1;"1 face";NULL;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;T;------;;;;"2021-11-16 15:27:20";"2022-01-19 14:40:58" +2;"2 faces";"2 faces -------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;-------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +3;"3 faces";"3 faces -------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;-------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +4;"4 faces";"4 faces -------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;-------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +AAP;"Arêtes et angles plaqués";"Arêtes et angles plaqués";15.24;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;ML;PLA;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +AC;ACCUEIL;"ACCUEIL =======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +AL;ALCOVE;"ALCOVE ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +AME;"Aménagement, mise en place et";"Aménagement, mise en place et repli y compris déplacements pour suivi :";250.00;0.00;250.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;ASS;"repli y compris déplacements pour suivi :";;;;"2020-01-15 15:55:21";"2021-10-27 09:10:00" +API;"Arrachage, préparations et pose de";"Arrachage, préparations et pose de papier ingrain :";21.50;16.27;18.80;18.00;22.00;16.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;"papier ingrain :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +APR;"Pose de revêtement mural sur préparations :";"Pose de revêtement mural sur préparations :";25.00;23.07;18.80;22.50;25.00;25.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +ASS;"Pose d'un déshumidificateur :";"Pose d'un assécheur :";30.00;0.00;30.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;ASS;;;;;"2020-01-15 15:50:50";"2021-10-27 09:08:49" +ASS26;"Assèchement ambiant habitation y ";NULL;0.00;343.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;ASS_26_ASSEC;;;;;;;;;;ASS;"compris amenée, repli et location";"du matériel d'un appareil limité à";"3 semaines :";;"2022-05-12 09:12:07";"2022-06-30 09:03:17" +ASS27;"Assèchement ambiant habitation par ";NULL;0.00;171.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;ASS_27_ASSEC;;;;;;;;;;ASS;"appareil supplémentaire limité à";"3 semaines :";;;"2022-06-30 09:03:48";"2022-06-30 09:12:52" +B;Boiseries;"Boiseries ---------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;---------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +BAL;BALCON;"BALCON ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +BU;BUREAU;"BUREAU ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +BUA;BUANDERIE;"BUANDERIE =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +C;"Coupes et arasements :";"Coupes et arasements :";1.50;1.50;1.50;1.50;1.50;1.50;1.50;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;0;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CA;"Mise en peinture de canalisations ou";"Mise en peinture de canalisations ou tuyauteries :";0.00;5.75;3.10;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_9_PEINTU;;;;;;;;;;PEI;"tuyauteries :";;;;"2019-06-12 12:08:15";"2019-06-12 12:09:05" +CAF005;"Démolition de faïence et reprise";NULL;0.00;0.00;0.00;0.00;0.00;0.00;15.29;0.00;0.00;0.00;;;;;;;CAF005;;;;;CAR;"d'enduit :";;;;"2021-11-30 17:21:29";"2021-11-30 17:22:22" +CAF010;"Fourniture et pose de faïence";NULL;0.00;0.00;0.00;0.00;0.00;0.00;46.72;0.00;0.00;0.00;;;;;;;CAF010;;;;;CAR;"blanche 25 * 33 non compris";"dépose :";;;"2021-11-30 17:25:37";"2021-11-30 17:27:03" +CAF036;"Fourniture et pose d'un tablier de";NULL;0.00;0.00;0.00;0.00;0.00;0.00;123.31;0.00;0.00;0.00;;;;;;;CAF036;;;;;CAR;"baignoire avec habillage faïence";"et trappe de visite :";;;"2021-11-30 17:28:34";"2022-03-30 15:34:50" +CAF043;"Fourniture et pose de faïence";NULL;0.00;0.00;0.00;0.00;0.00;0.00;50.95;0.00;0.00;0.00;;;;;;;CAF043;;;;;CAR;"Forfait sur lavabo :";;;;"2021-11-30 17:27:21";"2021-11-30 17:28:19" +CAF052;"Fourniture et pose de faïence";NULL;0.00;0.00;0.00;0.00;0.00;0.00;53.32;0.00;0.00;0.00;;;;;;;CAF052;;;;;CAR;"métro :";;;;"2021-11-30 17:22:51";"2022-03-30 15:35:01" +CAR0;"Prestations hors référentiel";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_0_HORBPU;;;;;;;;;;CAR;"tarifaire :";;;;"2019-08-07 08:57:16";"2022-01-18 08:53:56" +CAR10;"Fourniture et pose d'un tablier de";"Fourniture et pose d'un tablier de baignoire hors carrelage :";0.00;293.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_10_TABLI;;;;;;;;;;CAR;"baignoire hors carrelage :";;;;"2019-08-07 09:03:35";"2022-07-04 09:53:34" +CAR14;"Facturation minimale d'intervention";"Facturation minimale d'intervention carrelage :";0.00;330.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_14_FACTU;;;;;;;;;;CAR;"carrelage :";;;;"2019-08-07 08:58:24";"2019-08-07 08:59:31" +CAR15;"Remplacement de sous couche acoustique";NULL;0.00;45.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_15_REMPL;;;;;;;;;;CAR;"ou étanchéité sous carrelage y compris ";"arrachage, préparation des supports,";"revêtement ";;"2022-10-10 16:39:33";"2022-10-10 16:41:38" +CAR3;"Démolition, évacuation et pose de";"Démolition, évacuation et pose de carrelage au sol collé ou scellé y compris préparations :";0.00;79.10;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_3_DEMOL;;;;;;;;;;CAR;"carrelage au sol collé ou scellé y ";"compris préparations :";;;"2019-08-07 08:40:36";"2022-07-04 09:54:12" +CAR4;"Démolition, évacuation et pose de";"Démolition, évacuation et pose de carrelage aux murs, collé ou scellé y compris préparations :";0.00;73.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_4_DEMOL;;;;;;;;;;CAR;"carrelage aux murs, collé ou scellé";"y compris préparations : ";;;"2019-08-07 08:46:15";"2022-07-04 09:56:34" +CAR5;"Fourniture de carrelage ou faience :";"Fourniture de carrelage ou faience :";0.00;23.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_5_FOURN;;;;;;;;;;CAR;;;;;"2019-08-07 08:48:47";"2022-07-04 09:56:44" +CAR6;"Démolition, évacuation et pose de";"Démolition, évacuation et pose de plinthes collées ou scellées y compris préparations :";0.00;12.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_6_DEMOL;;;;;;;;;;CAR;"plinthes collées ou scellées y compris";"préparations :";;;"2019-08-07 08:49:50";"2022-07-04 09:56:59" +CAR7;"Fourniture de plinthes carrelage :";"Fourniture de plinthes carrelage :";0.00;20.10;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CAR_7_FOURN;;;;;;;;;;CAR;;;;;"2019-08-07 08:51:00";"2022-07-04 09:57:14" +CE;CELLIER;"CELLIER =======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CH;CHAMBRE;"CHAMBRE =======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CH1;"CHAMBRE 1";"CHAMBRE 1 =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CH2;"CHAMBRE 2";"CHAMBRE 2 =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CH3;"CHAMBRE 3";"CHAMBRE 3 =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CH4;"CHAMBRE 4";"CHAMBRE 4 =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CH5;"CHAMBRE 5";"CHAMBRE 5 =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CHA16;"Remplacement laine de verre en";"Remplacement laine de verre en kraft par 80 mm pour isolation des combles :";0.00;15.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;CHA_16_LAINE;;;;;;;;;;MEN;"kraft par 80 mm pour isolation";"des combles :";;;"2020-06-22 10:27:13";"2022-07-04 09:57:45" +CHE;"CHAMBRE ENFANT";"CHAMBRE ENFANT ==============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CHF;"CHAMBRE FILLE";"CHAMBRE FILLE =============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CHG;"CHAMBRE GARCON";"CHAMBRE GARCON ==============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CHP;"CHAMBRE PARENTS";"CHAMBRE PARENTS ===============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;===============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CM;"Cristallisation de marbre :";"Cristallisation de marbre :";53.00;53.00;53.00;53.00;53.00;53.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CO;COULOIR;"COULOIR =======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +COVID;"Forfait protocole sanitaire COVID";"Forfait protocole sanitaire COVID";27.28;27.28;27.28;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;T;;;;;"2020-05-07 08:42:11";"2021-01-04 10:07:39" +CR;"COIN REPAS";"COIN REPAS ==========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +CU;CUISINE;"CUISINE =======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +D;"Dépose de moquette collée ou revêtement";"Dépose de moquette collée ou revêtement PVC collé :";8.00;0.00;7.22;7.25;0.00;8.40;5.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;"PVC collé :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DAL;"Arrachage des dalles existantes, préparations";"Arrachage des dalles existantes, préparations des fonds et pose de dalles collées :";28.50;18.55;28.50;20.70;22.80;19.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;"des fonds et pose de dalles collées :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DBA;"Dépose du faux plafond existant :";"Dépose du faux plafond existant :";23.00;23.00;29.50;22.77;23.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DE;DEBARRAS;"DEBARRAS ========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DEC3;"Décontamination, nettoyage des ";NULL;0.00;4.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;DEC_3_DECON;;;;;;;;;;ASS;"embellissement Plafond et Murs,";"lessivage :";;;"2020-12-11 17:12:10";"2022-07-04 09:58:28" +DEG;DEGAGEMENT;"DEGAGEMENT ==========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DMP;"Doublage des murs en placo :";"Doublage des murs en placo :";48.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DO;DOUCHE;"DOUCHE ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DP;"Déplacement :";"Déplacement :";0.00;49.51;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DPP;"Dépose du revêtement de sol existant,";"Dépose du revêtement de sol existant, préparations des fonds et pose de revêtement collé :";25.50;21.16;21.30;25.88;21.00;16.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;"préparations des fonds et pose de revêtement";"collé :";;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +DR;DRESSING;"DRESSING ========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +E;"Effeuillage, reprise d'enduit :";"Effeuillage, reprise d'enduit :";0.00;21.20;12.00;9.00;9.78;11.00;10.00;0.00;0.00;0.00;;PEN_22_REBOU;;;;;;;;;;PNT;;;;;"2017-08-15 09:18:37";"2022-07-08 15:55:04" +EN;ENTREE;"ENTREE ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +EPD;"Enduit plâtre sur dégrossissage :";"Enduit plâtre sur dégrossissage :";52.00;19.50;22.28;0.00;0.00;38.00;0.00;0.00;0.00;0.00;;PEN_22_REBOU;;;;;;;;;;PLA;;;;;"2017-08-15 09:18:37";"2019-06-06 10:26:25" +ER;"Effeuillage, remaillage :";"Effeuillage, remaillage :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;0;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +F;"Fourniture :";"Fourniture :";35.00;35.00;35.00;35.00;35.00;35.00;35.00;0.00;0.00;0.00;;PEN_8_FOURNI;;;;;;;;;;PNT;;;;;"2017-08-15 09:18:37";"2021-09-30 10:01:28" +FBS;"Fourniture et pose de barre de seuil :";"Fourniture et pose de barre de seuil :";25.30;17.00;25.30;14.28;17.50;17.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;SOL;;;;;"2017-08-15 09:18:37";"2022-02-07 09:22:17" +FMI;"Forfait minimun d'intervention :";"Forfait minimun d'intervention :";400.00;310.00;400.00;324.00;250.00;310.00;300.00;0.00;0.00;0.00;;PEN_21_FACTU;;;;;;;;;;PNT;;;;;"2017-08-15 09:18:37";"2022-02-10 09:24:30" +FMP;"Forfait minimun d'intervention pour";"Forfait minimun d'intervention pour travaux de plâtrerie :";240.00;240.00;150.00;240.00;240.00;240.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;"travaux de plâtrerie :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +FMS;"Forfait minimun shampoinage :";"Forfait minimun shampoinage :";150.00;150.00;150.00;150.00;150.00;150.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +FS;"Fourniture en m² :";"Fourniture en m² :";28.00;28.00;28.00;28.00;28.00;28.00;28.00;0.00;0.00;0.00;;PEN_16_FOUR;;;;;;;;;;PP;;;;;"2020-01-21 16:23:16";"2020-01-21 16:25:46" +G;GARAGE;"GARAGE ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +GEN0;"Traitement responsable des";NULL;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;GEN_0_DECHET;;;;;;;;;;T;"déchets selon règlementation :";;;;"2022-02-07 11:54:58";"2022-02-07 11:56:04" +GO;"Application de peinture projetée type";"Application de peinture projetée type gouttelettes sur murs ou plafond";0.00;28.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_4_APPLIC;;;;;;;;;;PEI;"gouttelettes sur murs ou plafond";;;;"2019-06-12 12:02:21";"2019-06-12 12:03:19" +H;"HALL D'ENTREE";"HALL D'ENTREE =============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +HB;"Prestations hors référentiel";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_0_HORBPU;;;;;;;;;;T;"tarifaire :";;;;"2019-06-12 12:11:36";"2021-01-19 08:53:27" +I;"Application d'une couche d'impression :";"Application d'une couche d'impression :";12.00;12.00;12.00;12.00;12.00;12.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PEI;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +IF;"Isolation des fumées par application";"Isolation des fumées par application d'un vernis :";12.96;12.96;12.96;12.96;12.96;12.96;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PEI;"d'un vernis :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +IPN;"Impression du plâtre ou du placoplâtre";"Impression du plâtre ou du placoplâtre neuf :";10.00;10.00;10.00;10.00;10.00;10.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PEI;"neuf :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +L;"Lessivage :";"Lessivage :";8.00;8.00;8.00;8.05;8.00;8.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;0;PEI;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +LC;"Lessivage à conserver :";"Lessivage à conserver :";10.00;5.98;6.10;8.05;5.50;8.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;0;PEI;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +M;Murs;"Murs ----";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;----;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +MAC0;"Prestation hors référentiel";NULL;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MAC_0_HORBPU;;;;;;;;;;MEN;"tarifaire :";;;;"2022-01-18 08:50:42";"2022-01-18 08:54:14" +MAC10;"Briques pleines ordinaires avec joint";NULL;0.00;113.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MAC_10_BRIQ;;;;;;;;;;PLA;"1 cm, paroi de 10,5 cm ép :";;;;"2022-09-30 08:14:54";"2022-09-30 08:18:01" +MAC11;"Briques creuses dite plâtrirère";NULL;0.00;30.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MAC_11_BRIQU;;;;;;;;;;PLA;"en pose traditionnelle, ép 5 cm :";;;;"2022-09-30 14:01:19";"2022-09-30 14:02:23" +MAC3;"Démolition maçonnerie toutes";NULL;0.00;320.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MAC_03_DEMOL;;;;;;;;;;PLA;"natures de matériaux y compris";"fondations :";;;"2022-09-30 13:59:58";"2022-09-30 14:01:13" +MB;"Murs et boiseries";"Murs et boiseries -----------------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;-----------------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +ME;"MONTEE D'ESCALIERS";"MONTEE D'ESCALIERS ==================";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==================;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +MEN0;"Prestations hors référentiel";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_0_HORBPU;;;;;;;;;;MEN;"tarifaire :";;;;"2019-06-19 14:16:37";"2020-03-12 17:10:05" +MEN10;"Remplacement de fenêtre, 1 à 2 vantaux";"Remplacement de fenêtre, 1 à 2 vantaux sur cadre existant, y compris dépose, réglages, ajustages, hors fourniture :";0.00;164.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_10_REMPL;;;;;;;;;;MEN;"sur cadre existant, y compris dépose,";"réglages, ajustages, hors fourniture :";;;"2019-11-27 15:17:15";"2022-07-04 10:02:26" +MEN12;"Plus value par vantail suppl.";NULL;0.00;52.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_12_PLUSV;;;;;;;;;;MEN;"pour pose sur cadre existant ou";"scellée porte, porte-fenêtre et";"fenêtre :";;"2020-12-07 14:59:37";"2022-07-04 10:02:42" +MEN16;"Pose de porte de garage";"Remplacement porte de garage taille standard coulissante y compris dépose et toutes sujetions de calage, fixations et équipement de quincaillerie :";0.00;507.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_16_REMPL;;;;;;;;;;MEN;"taille standard coulissante y compris";"dépose et toutes sujetions de";"calage, fixations et équipement de";"quincaillerie :";"2020-03-16 15:20:29";"2022-07-04 11:51:12" +MEN17;"Pose de porte de garage";"Remplacement porte de garage sectionnelle taille standard y compris dépose et toutes sujétions de calage fixations et équipement de quincaillerie";0.00;578.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_17_REMPL;;;;;;;;;;MEN;"sectionnelle taille standard y compris";"dépose et toutes sujétions de calage";"fixations et équipement de quincaillerie";;"2019-09-13 16:44:03";"2022-07-04 11:52:05" +MEN18;"Pose de porte de garage basculante";"Remplacement porte de garage basculante taille standard avec ou sans débordement y compris dépose et toutes sujetions de calage, fixation et équipement de quincaillerie :";0.00;466.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_18_REMPL;;;;;;;;;;MEN;"taille standard avec ou sans débordement";" y compris dépose et toutes sujetions de";"calage, fixation et équipement de";"quincaillerie :";"2019-07-16 17:06:49";"2022-07-04 11:52:22" +MEN24;"Dépose et repose ou remplacement ";"Dépose et repose ou remplacement de tout type de volets roulants manuels Surface inférieure à 2 m² / hors fourniture :";0.00;167.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_24_DEPOS;;;;;;;;;;MEN;"de tout type de volets roulants manuels";"Surface inférieure à 2 m² / hors fourniture :";;;"2019-09-05 08:55:51";"2022-07-04 10:04:34" +MEN25;"Dépose et repose ou remplacement";"Dépose et repose ou remplacement de tout type de volets roulants manuels Surface entre 2 et 4 m² / hors fourniture :";0.00;248.90;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_25_DEPOS;;;;;;;;;;MEN;"de tout type de volets roulants manuels";"Surface entre 2 et 4 m² / hors fourniture :";;;"2019-09-05 09:25:53";"2022-07-04 10:04:46" +MEN26;"Dépose et repose ou remplacement de tout ";"Dépose et repose ou remplacement de tout type de volets roulants électriques, surface infé 2m², hors fournitures avec conservation des branchements existants :";0.00;185.10;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_26_DEPOS;;;;;;;;;;MEN;"type de volets roulants électriques, surface ";"infé 2m², hors fournitures avec conservation";"des branchements existants :";;"2019-07-16 16:51:10";"2022-07-04 10:04:58" +MEN27;"Dépose et repose ou remplacement de ";"Dépose et repose ou remplacement de tout type de volets roulants électrique surf entre 2 et 4 m² hors fournitures avec conservation des branchements existants :";0.00;274.90;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_27_DEPOS;;;;;;;;;;MEN;"tout type de volets roulants électrique";"surf entre 2 et 4 m² hors fournitures avec";"conservation des branchements existants :";;"2019-07-16 16:46:04";"2022-07-04 10:05:09" +MEN28;"Dépose et repose ou remplacement";NULL;0.00;89.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_28_DEPOS;;;;;;;;;;T;"de tout type de volets battants";"à l'unité par volet y compris";"scellements, réglages, ajustages";"hors fourniture :";"2021-08-05 16:05:45";"2022-07-04 10:05:18" +MEN30;"Reprise sur PVC de < 3 impacts, ";"Reprise sur PVC de < 3 impacts, trous, éclats sur un ou plusieurs ouvrages, y compris toutes sujetions thermo-reprofilage, dépose et repose, nettoyage, ponçage :";0.00;220.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_30_REPRI;;;;;;;;;;MEN;"trous, éclats sur un ou plusieurs ";"ouvrages, y compris toutes sujetions";"thermo-reprofilage, dépose et";"repose, nettoyage, ponçage :";"2020-07-23 16:49:04";"2020-07-23 16:54:25" +MEN31;"Reprise sur PVC, > 4 et <6 impacts, ";"Reprise sur PVC, > 4 et <6 impacts, trous, éclats sur ouvrage, y compris toutes sujétions thermo reprofilage Dépose et repose éventuelles, nettoyages, ponçages, lustrage :";0.00;451.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_31_REPRI;;;;;;;;;;MEN;"trous, éclats sur ouvrage, y compris";"toutes sujétions thermo reprofilage";"Dépose et repose éventuelles, ";"nettoyages, ponçages, lustrage :";"2020-02-13 09:56:20";"2020-02-13 09:59:07" +MEN32;"Reprise sur PVC > 7 impacts,";NULL;0.00;671.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_32_REPRI;;;;;;;;;;MEN;"trous, éclats sur un ou plusieurs";"ouvrages, y compris toutes sujétions";"thermo-reprofilage, dépose et repose,";"nettoyage, lustrages :";"2020-12-07 16:22:57";"2020-12-07 16:26:30" +MEN35;"Remplacement de tout type de joint";NULL;0.00;59.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_35_REM;;;;;;;;;;MEN;"d'étanchéité par vantail de fenêtre,";"porte-fenêtre, porte :";;;"2020-12-07 16:27:37";"2022-07-04 10:05:43" +MEN38;"Réparation sur menuiserie alu";NULL;0.00;850.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_38_REPA;;;;;;;;;;MEN;"d'enfoncements de matières :";;;;"2022-10-19 16:41:20";"2022-10-26 11:44:48" +MEN4;"Remplacement d'un bloc porte";"Remplacement d'un bloc porte d'entrée en maison sur cadre existant y compris dépose, pose verrous et/ou serrures, réglages ajustage et toutes sujétions :";0.00;258.40;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_4_REMP;;;;;;;;;;MEN;"d'entrée en maison sur cadre";"existant y compris dépose, pose";"verrous et/ou serrures, réglages";"ajustage et toutes sujétions :";"2020-01-08 16:14:58";"2022-07-04 10:05:58" +MEN5;"Remplacement bloc porte d'entrée sur";"Remplacement bloc porte d'entrée sur cadre existant, y compris dépose, pose de verrous et/ou serrures, réglages, ajustages hors fourniture :";0.00;186.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_5_REMPL;;;;;;;;;;MEN;"cadre existant, y compris dépose, pose de";"verrous et/ou serrures, réglages, ajustages";"hors fourniture :";;"2019-11-14 08:50:42";"2022-07-04 10:06:08" +MEN6;"Remplacement bloc porte d'entrée de";"Remplacement bloc porte d'entrée de cave, y compris dépose, scellements pose de verrous et/ou serrures réglages, ajustage, hors fourniture :";0.00;131.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_6_REMPL;;;;;;;;;;MEN;"cave, y compris dépose, scellements";"pose de verrous et/ou serrures";"réglages, ajustage, hors fourniture :";;"2019-11-14 08:44:13";"2022-07-04 10:06:19" +MEN7;"Remplacement d'un montant dormant";"Remplacement d'un montant dormant de l'huisserie d'une porte ou porte fenêtre y compris dépose, scellements, réglages ajustages, repose, fournitures :";0.00;249.90;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_7_REMPL;;;;;;;;;;MEN;"de l'huisserie d'une porte ou porte fenêtre";"y compris dépose, scellements, réglages";"ajustages, repose, fournitures :";;"2019-11-14 08:46:49";"2022-07-04 10:06:29" +MEN9;"Remplacement de porte-fenêtre";"Remplacement de porte-fenêtre 2 vantaux sur cadre existant y compris dépose, réglages, ajustages, hors fourniture :";0.00;238.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MEN_9_REMPL;;;;;;;;;;MEN;"2 vantaux sur cadre existant y compris";"dépose, réglages, ajustages, hors";"fourniture :";;"2019-11-27 15:14:28";"2022-07-04 10:06:47" +MEZ;MEZZANINE;"MEZZANINE =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +MIR0;"Prestations hors référentiel";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_0_HORBPU;;;;;;;;;;MIR;"tarifaire :";;;;"2019-12-13 11:09:45";"2020-03-12 17:10:15" +MIR24;"Remplacement double vitrage";"Remplacement double vitrage isolants préfabriqués une face clair ep.04 mm y compris traitement des déchets :";0.00;101.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_24_DOUBL;;;;;;;;;;MIR;"isolants préfabriqués une face";"clair ep.04 mm y compris";"traitement des déchets :";;"2020-02-21 08:42:34";"2022-07-04 10:10:53" +MIR25;"Remplacement d'un double vitrage";NULL;0.00;132.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_25_REMPL;;;;;;;;;;MIR;"isolants préfabriqués une face";"clair ep. 5 ou 6 mm y compris";"intercalaires :";;"2021-05-25 08:46:17";"2022-09-20 16:24:39" +MIR26;"Remplacement double vitrage isolant";"Remplacement double vitrage isolant préfabriqué ep. 08 mm y compris traitements des déchets :";0.00;159.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_26_DOUBL;;;;;;;;;;MIR;"préfabriqué ep. 08 mm y compris";"traitements des déchets :";;;"2020-01-08 14:31:06";"2022-07-04 10:11:08" +MIR27;"Remplacement double vitrage ";NULL;0.00;190.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_27_DOUB;;;;;;;;;;MIR;"isolants 1 face claire ép. 8 mm :";;;;"2022-10-07 08:47:45";"2022-10-07 08:50:19" +MIR28;"Plus value pour protection";"Plus value pour protection thermique :";0.00;25.90;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_28_PLUVA;;;;;;;;;;MIR;"thermique :";;;;"2020-01-08 14:34:00";"2022-07-04 10:11:20" +MIR29;"Plus value pour remplissage gaz";"Plus value pour remplissage gaz argon :";0.00;5.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_29_PLUSV;;;;;;;;;;MIR;"argon : ";;;;"2020-02-21 08:44:47";"2022-07-04 10:11:35" +MIR44;"Déplacement, prise de mesure,";"Forfait déplacement, prise de mesure, mesure, déplacement inclus hors urgence et fermeture provisoire :";0.00;41.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_44_FORF;;;;;;;;;;MIR;"hors urgence et fermetures provisoires :";;;;"2020-08-03 15:29:15";"2022-09-20 16:30:44" +MIR45;"Déplacement pose glace inférieure";NULL;0.00;59.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_45_FORF;;;;;;;;;;MIR;"à 2 m² hors urgences et fermetures";"provisoires :";;;"2022-09-20 16:22:07";"2022-09-20 16:27:02" +MIR51;"Remplacement vitrage feuilletés";"Remplacement vitrage feuilletés 33-2, y compris traitement des déchets :";0.00;198.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_51_VITRF;;;;;;;;;;MIR;"33-2, y compris traitement des";"déchets :";;;"2020-08-03 15:32:59";"2022-07-04 10:15:24" +MIR52;"Remplacement vitrage feuilletés";"Mise en sécurité d'une porte fenêtre par pose d'un contre plaqué ou d'un film armé y compris déplacement et traitement des déchets :";0.00;216.90;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;MIR_52_VITRF;;;;;;;;;;MIR;"44-2 y compris démasticage :";;;;"2020-02-21 08:46:02";"2022-07-04 10:16:45" +MO;"Fourniture et pose de molleton :";"Fourniture et pose de molleton :";23.00;23.00;23.00;23.00;23.00;23.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PP;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +MP;"Murs et plafond";"Murs et plafond ---------------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;---------------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +MPB;"Murs, plafond et boiseries";"Murs, plafond et boiseries --------------------------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;--------------------------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +P;Plafond;"Plafond -------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;-------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PAR0;"Prestations hors référentiel ";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_0_HORBPU;;;;;;;;;;PAR;"tarifaire :";;;;"2019-06-19 12:01:07";"2020-03-12 17:10:26" +PAR10;"Fourniture de sous couche :";NULL;0.00;2.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_10_FOUR;;;;;;;;;;PAR;;;;;"2021-09-06 08:58:56";"2022-07-04 10:17:24" +PAR11;"Remplacement parquet flottant ou";"Remplacement parquet flottant ou stratifié, dépose, évacuation, pose y compris sous couche :";0.00;34.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_11_REMPL;;;;;;;;;;PAR;"stratifié, dépose, évacuation, pose y ";"compris sous couche :";;;"2019-06-19 11:53:55";"2022-07-04 10:17:38" +PAR12;"Fourniture parquet stratifié ";"Fourniture parquet stratifié classique :";0.00;17.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_12_FOURN;;;;;;;;;;PAR;"classique :";;;;"2019-09-19 10:34:52";"2022-07-04 10:17:48" +PAR15;"Fourniture et pose de barre de";"Fourniture et pose de barre de seuil :";0.00;16.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_15_BARRE;;;;;;;;;;PAR;"seuil :";;;;"2019-06-19 14:15:31";"2022-07-04 10:18:02" +PAR16;"Remplacement plinthes ou quart de";"Remplacement plinthes ou quart de rond, dépose, fourniture et pose :";0.00;15.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_16_REMPL;;;;;;;;;;PAR;"rond, dépose, fourniture et pose :";;;;"2019-06-19 14:14:05";"2022-07-04 10:18:15" +PAR17;"Dépose, fourniture et pose de";NULL;0.00;25.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_17_DEPO;;;;;;;;;;PAR;"panneau de type OSB";;;;"2022-03-31 14:37:29";"2022-07-04 10:18:25" +PAR18;"Ragréage sol y compris fournitures :";"Ragréage sol y compris fournitures :";0.00;11.90;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_18_RAGRE;;;;;;;;;;PAR;;;;;"2019-07-16 17:18:57";"2022-07-04 10:18:40" +PAR19;"Facturation minimale parquet,";"Facturation minimale parquet, toutes sujétions y compris fourniture";0.00;170.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_19_FACTU;;;;;;;;;;PAR;"toutes sujétions y compris fourniture";;;;"2019-07-16 17:20:44";"2021-09-06 09:00:15" +PAR3;"Remplacement parquet contrecollé,";"Remplacement parquet contrecollé, dépose, préparation et pose :";0.00;66.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_3_REMPLA;;;;;;;;;;PAR;"dépose, préparation et pose :";;;;"2019-06-19 14:19:21";"2022-07-04 10:19:07" +PAR4;"Fourniture parquet contrecollé ";"Fourniture parquet contrecollé standard :";0.00;49.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_4_FOURN;;;;;;;;;;PAR;"standard :";;;;"2020-01-08 16:45:39";"2022-07-04 10:19:20" +PAR6;"Remplacement parquet massif";NULL;0.00;59.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_6_REMP;;;;;;;;;;PAR;"sur lambourdes y compris dépose";"et pose du parquet :";;;"2020-12-07 16:16:44";"2022-07-04 10:19:32" +PAR7;"Fourniture parquet lame bois massif";"Fourniture parquet lame bois massif en chêne :";0.00;99.40;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_7_FOURNI;;;;;;;;;;PAR;"en chêne :";;;;"2019-06-19 14:11:59";"2022-07-04 10:19:47" +PAR8;"Fourniture parquet lames bois massif";"Fourniture parquet lames bois massif en pin :";0.00;60.10;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_8_FOURNI;;;;;;;;;;PAR;"en pin :";;;;"2019-06-19 14:10:34";"2022-07-04 10:20:01" +PAR9;"Vitrification du parquet y compris ";"Vitrification du parquet y compris ponçage et préparations :";26.50;36.90;26.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PAR_9_VITRIF;;;;;;;;;;PAR;"ponçage et préparations :";;;;"2019-06-19 14:21:47";"2022-07-04 10:20:14" +PAV;"Ponçage, affleurage, vernissage :";"Ponçage, affleurage, vernissage :";26.93;26.93;27.20;23.00;25.20;22.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PD;"Pose collée de dalles :";"Pose collée de dalles :";12.00;17.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PEE;"Piquage, effeuillage, encollage :";"Piquage, effeuillage, encollage :";23.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PEN0;"Prestations hors référentiel";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_0_HORBPU;;;;;;;;;;PEI;"tarifaire :";;;;"2020-01-08 11:07:47";"2020-03-12 17:10:35" +PEN10;"Peinture porte par face, huisserie et ";"Peinture porte 2 faces, huisserie et boiseries ou radiateur :";0.00;40.40;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_10_PEINT;;;;;;;;;;PEI;"boiseries ou radiateur :";;;;"2019-11-20 17:35:07";"2022-07-04 10:20:47" +PEN11;"Mise en peinture radiateur";NULL;0.00;78.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_11_PEINT;;;;;;;;;;PEI;"fonte :";;;;"2021-09-06 08:52:52";"2022-07-04 10:20:59" +PEN13;"Dépose et pose de revêtement de sol,";"Dépose et pose de revêtement de sol, préparation du support :";0.00;18.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_13_MOQU;;;;;;;;;;SOL;"préparation du support :";;;;"2019-10-21 09:20:58";"2022-07-04 10:21:10" +PEN17;"Remplacement de plinthes ou quart de";"Remplacement de plinthes ou quart de rond, dépose, fourniture et pose :";0.00;14.40;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_17_REMPL;;;;;;;;;;SOL;"rond, dépose, fourniture et pose :";;;;"2019-10-21 09:22:26";"2022-07-04 10:21:23" +PEN18;"Dépose, fourniture et pose de barre";"Dépose, fourniture et pose de barre de seuil :";0.00;14.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_18_BARRE;;;;;;;;;;SOL;"de seuil :";;;;"2019-10-21 09:23:32";"2022-07-04 10:21:39" +PEN2;"Mise en peinture sur murs y compris";"Mise en peinture sur murs y compris reprise d'enduit, et préparations :";0.00;20.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_2_MISEEN;;;;;;;;;;PEI;"reprise d'enduit, et préparations :";;;;"2019-10-21 08:59:25";"2022-07-04 10:21:49" +PEN21;"Facturation minimale peinture, ";"Facturation minimale peinture, toutes sujétions y compris fourniture :";0.00;310.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_21_FACTU;;;;;;;;;;PEI;"toutes sujétions y compris ";"fournitures :";;;"2020-02-13 10:03:25";"2022-02-16 11:27:17" +PEN22;"Rebouchage des fissures, grattage,";"Rebouchage des fissures, grattage, reprise d'enduit :";0.00;21.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_22_REBOU;;;;;;;;;;PLA;"reprise d'enduit :";;;;"2019-10-21 09:19:04";"2022-07-04 10:22:08" +PEN3;"Mise en peinture sur plafond y ";"Mise en peinture sur plafond y compris préparations des fonds :";0.00;22.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_3_MISEEN;;;;;;;;;;PEI;"compris préparations des fonds :";;;;"2019-10-21 09:02:10";"2022-07-04 10:22:17" +PEN4;"Application de peinture projetée";"Application de peinture projetée type gouttelette sur murs et/ou plafond, intégrant grattage, rebouchage, enduit et ponçage :";0.00;31.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_4_APPLIC;;;;;;;;;;PEI;"type gouttelette sur murs et/ou";"plafond, intégrant grattage,";"rebouchage, enduit et ponçage :";;"2020-06-30 08:41:24";"2022-07-04 10:22:35" +PEN6;"Remplacement de toile de verre, remise";"Remplacement de toile de verre, remise en peinture y compris arrachage de la toile, et préparations des fonds";0.00;33.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_6_REMPL;;;;;;;;;;PEI;"en peinture y compris arrachage de la";"toile et préparations des fonds";;;"2019-10-21 09:13:10";"2022-07-04 10:22:45" +PEN7;"Remplacement du papier peint y compris";"Remplacement du papier peint y compris arrachage et préparations :";0.00;20.10;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_7_REMPL;;;;;;;;;;PP;"arrachage et préparations : ";;;;"2019-10-21 09:15:26";"2022-07-04 10:22:55" +PEN8;"Fourniture papier peint classique :";"Fourniture papier peint classique :";0.00;19.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_8_FOURN;;;;;;;;;;PP;;;;;"2019-10-21 09:17:07";"2022-02-07 09:28:06" +PEN9;"Mise en peinture des plinthes ou ";"Mise en peinture des plinthes ou canalisation :";0.00;6.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_9_PEINT;;;;;;;;;;PEI;"canalisation :";;;;"2019-10-21 09:18:01";"2022-07-04 10:23:10" +PF;"Préparations des fonds :";"Préparations des fonds :";8.50;6.02;0.00;8.28;0.00;0.00;6.50;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PFR;"Pose de frise :";"Pose de frise :";10.00;10.00;3.60;10.00;10.00;10.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;ML;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PG;"Peinture laque brillante sur";"Peinture laque brillante sur préparations :";28.00;18.60;20.90;23.60;23.50;20.00;34.00;0.00;0.00;0.00;;;;;;;;;;;;PEI;"préparations :";;;;"2017-08-15 09:18:37";"2022-10-20 15:45:31" +PIF;"Peinture d'impression des fonds :";"Peinture d'impression des fonds :";12.96;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PEI;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PL;"Mise en peinture des plinthes ou";"Mise en peinture des plinthes ou tuyaux :";0.00;6.30;6.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_9_PEINTU;;;;;;;;;;PEI;"tuyaux :";;;;"2019-06-12 12:06:07";"2022-07-08 16:12:47" +PLA;Placard;"Placard -------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;-------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PLA0;"Prestations hors référentiel";"Prestations hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_0_HORBPU;;;;;;;;;;PLA;"tarifaire :";;;;"2019-10-25 16:12:14";"2020-03-12 17:10:44" +PLA12;"Remplacement faux plafond en BA13";"Remplacement faux plafond en BA13 sur ossature conservée, dépose, évacuation, fourniture et pose y compris bandes :";0.00;37.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_12_REMPL;;;;;;;;;;PLA;"sur ossature conservée, dépose,";"évacuation, fourniture et pose y compris";"bandes :";;"2019-10-25 16:08:15";"2022-07-04 10:23:34" +PLA13;"Remplacement faux plafond BA13";"Remplacement faux plafond BA13 y compris ossature métallique, dépose, évacuation, fourniture et pose y compris bandes :";0.00;53.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_13_REMPL;;;;;;;;;;PLA;"y compris ossature métallique, dépose,";"évacuation, fourniture et pose y compris";"bandes :";;"2019-10-25 16:10:38";"2022-07-04 10:23:44" +PLA14;"Remplacement dalles suspendues";"Remplacement dalles suspendues sur ossature métallique conservée, dépose, évacuation, fourniture et pose :";0.00;26.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_14_REMPL;;;;;;;;;;PLA;"sur ossature métallique conservée,";"dépose, évacuation, fourniture et";"pose :";;"2020-03-16 10:22:27";"2022-07-04 10:23:53" +PLA15;"Remplacement dalles suspendues";"Remplacement dalles suspendues y compris ossature métallique apparente, dépose, évacuation, fourniture et pose :";0.00;53.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_15_REMPL;;;;;;;;;;PLA;"y compris ossature métallique";"apparente, dépose, évacuation,";"fourniture et pose :";;"2020-03-16 10:20:58";"2022-07-04 10:24:01" +PLA16;"Remplacement isolant en plafond";"Remplacement isolant en plafond ép. jusqu'à 240 mm, dépose, évacuation, fourniture et pose :";0.00;25.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_16_REMPL;;;;;;;;;;PLA;"ép. jusqu'à 240 mm, dépose,";"évacuation, fourniture et pose :";;;"2020-08-03 09:56:01";"2022-07-04 10:24:16" +PLA17;"Reconstitution de plâtre manuel ";NULL;0.00;24.40;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_17_RECON;;;;;;;;;;PLA;"sur support brut sur murs et plafonds :";;;;"2022-02-08 16:23:05";"2022-07-04 10:26:46" +PLA5;"Remplacement de cloison ou doublage";"Remplacement de cloison ou doublage en plaques de plâtre BA 13 sur ossature conservée, dépose, fourniture et pose y compris bande :";0.00;32.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_5_REMPL;;;;;;;;;;PLA;"en plaques de plâtre BA 13 sur ossature";"conservée, dépose, fourniture et pose";"y compris bande :";;"2019-10-25 15:56:59";"2022-07-04 10:27:00" +PLA6;"Remplacement doublage en plaques";"Remplacement doublage en plaques de plâtre type BA13 sur rails y compris ossature métallique, dépose, évacuation, fourniture et pose :";0.00;50.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_6_REMPL;;;;;;;;;;PLA;"de plâtre type BA13 sur rails y";"compris ossature métallique, dépose,";"évacuation, fourniture et pose :";;"2019-10-25 15:58:56";"2022-07-04 10:27:12" +PLA7;"Remplacement cloison type BA13 Placostil";"Remplacement cloison type BA13 Placostil y compris ossature métallique, dépose, évacuation, fourniture et pose :";0.00;59.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_7_REMPL;;;;;;;;;;PLA;"y compris ossature métallique, dépose,";"évacuation, fourniture et pose :";;;"2019-10-25 16:02:42";"2022-07-04 10:27:34" +PLA9;"Plus value hydrofuges pour plaques";"Plus value hydrofuges pour plaques de plâtre type BA13 :";0.00;3.30;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLA_9_PLUSV;;;;;;;;;;PLA;"de plâtre type BA13 :";;;;"2020-02-21 15:52:02";"2022-07-04 10:29:14" +PLAT;PLATRERIE;"PLATRERIE =========";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=========;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PLI;Plinthes;"Plinthes --------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;--------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PLO14;"Dépose et repose de sanitaires";NULL;0.00;155.60;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLO_14_DEPO;;;;;;;;;;PLO;"scellés y compris accessoires";"et protections :";;;"2022-11-09 09:47:58";"2022-11-09 09:51:29" +PLO7;"Travaux de réparations réseau";NULL;0.00;47.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PLO7;;;;;;;;;;PLO;"d'alimentation en cuivre :";;;;"2022-11-09 09:51:36";"2022-11-09 09:52:49" +PLP033;"Entoilage :";NULL;0.00;0.00;0.00;0.00;0.00;0.00;5.95;0.00;0.00;0.00;;;;;;;PLP033;;;;;PP;;;;;"2021-11-30 17:16:55";"2021-11-30 17:18:09" +PLP034;"Peinture de sol :";NULL;0.00;0.00;0.00;0.00;0.00;0.00;12.75;0.00;0.00;0.00;;;;;;;PLP034;;;;;PEI;;;;;"2021-11-30 17:20:02";"2021-11-30 17:21:01" +PLP047;"Murs : Lessivage + Masticage +";NULL;0.00;0.00;0.00;0.00;0.00;0.00;5.95;0.00;0.00;0.00;;;;;;;PLP047;;;;;PEI;"Peinture satinée à 2 couches :";;;;"2021-11-30 17:18:39";"2021-11-30 17:19:50" +PLP093;"Plafond : Dépose dalles polystyrène +";NULL;0.00;0.00;0.00;0.00;0.00;0.00;10.19;0.00;0.00;0.00;;;;;;;PLP093;;;;;PEI;"Masticage + Raccord plâtre +";"Peinture glycéromate à 2 couches :";;;"2021-11-30 17:11:02";"2021-11-30 17:14:31" +PLP130;"Dépose et évacuation lambris";NULL;0.00;0.00;0.00;0.00;0.00;0.00;8.50;0.00;0.00;0.00;;;;;;;PLP130;;;;;MEN;"compris ossature :";;;;"2021-11-30 17:15:05";"2021-11-30 17:16:33" +PLP168;"Réfection complète d'un séjour :";NULL;0.00;0.00;0.00;0.00;0.00;0.00;475.57;0.00;0.00;0.00;;;;;;;PLP168;;;;;PEI;;;;;"2021-11-16 15:23:35";"2021-11-16 15:24:26" +PLP169;"Réfection complète d'une ";NULL;0.00;0.00;0.00;0.00;0.00;0.00;356.68;0.00;0.00;0.00;;;;;;;PLP169;;;;;PEI;"chambre :";;;;"2021-11-16 15:24:41";"2021-11-16 15:25:41" +PLP170;"Réfection complète Entrée";"1 face ------";0.00;0.00;0.00;0.00;0.00;0.00;174.10;0.00;0.00;0.00;;;;;;;PLP170;;;;;PEI;"ou petit dégagement :";;;;"2017-08-15 09:18:37";"2021-11-16 15:28:19" +PLP171;"Réfection complète d'un grand";NULL;0.00;0.00;0.00;0.00;0.00;0.00;207.22;0.00;0.00;0.00;;;;;;;PLP171;;;;;PEI;"dégagement :";;;;"2021-11-16 15:30:44";"2021-11-16 15:31:40" +PLP172;"Réfection complète de la";NULL;0.00;0.00;0.00;0.00;0.00;0.00;386.41;0.00;0.00;0.00;;;;;;;PLP172;;;;;PEI;"cuisine :";;;;"2021-11-16 15:31:44";"2021-11-16 15:32:24" +PLP173;"Réfection complète de la salle";NULL;0.00;0.00;0.00;0.00;0.00;0.00;245.43;0.00;0.00;0.00;;;;;;;PLP173;;;;;PEI;"de bains :";;;;"2021-11-16 15:32:34";"2021-11-16 15:33:13" +PLP174;"Réfection complète des";NULL;0.00;0.00;0.00;0.00;0.00;0.00;150.32;0.00;0.00;0.00;;;;;;;PLP174;;;;;PEI;"toilettes :";;;;"2021-11-16 15:33:18";"2021-11-16 15:34:05" +PLP177;"Réfection totale d'un logement";NULL;0.00;0.00;0.00;0.00;0.00;0.00;1698.47;0.00;0.00;0.00;;;;;;;"PLP177 ";;;;;PEI;"de type 1 :";;;;"2021-11-16 14:52:02";"2021-11-16 15:10:50" +PLP178;"Réfection totale d'un logement";NULL;0.00;0.00;0.00;0.00;0.00;0.00;2123.09;0.00;0.00;0.00;;;;;;;PLP178;;;;;PEI;"de type T2 ou T1B :";;;;"2021-11-16 15:11:16";"2021-11-16 15:12:50" +PLP179;"Réfection totale d'un logement";NULL;0.00;0.00;0.00;0.00;0.00;0.00;2554.43;0.00;0.00;0.00;;;;;;;PLP179;;;;;PEI;"de type T3 ou T2B :";;;;"2021-11-16 15:13:52";"2022-03-30 15:34:32" +PLP180;"Réfection totale d'un logement";NULL;0.00;0.00;0.00;0.00;0.00;0.00;2802.48;0.00;0.00;0.00;;;;;;;PLP180;;;;;PEI;"de type T4 ou T3B :";;;;"2021-11-16 15:15:49";"2021-12-01 08:55:47" +PLP181;"Réfection totale d'un logement";NULL;0.00;0.00;0.00;0.00;0.00;0.00;3227.09;0.00;0.00;0.00;;;;;;;PLP181;;;;;PEI;"de type T5 ou T4B :";;;;"2021-11-16 15:17:32";"2021-11-16 15:18:43" +PLP182;"Réfection totale d'un logement";NULL;0.00;0.00;0.00;0.00;0.00;0.00;3481.86;0.00;0.00;0.00;;;;;;;PLP182;;;;;PEI;"de type T6";;;;"2021-11-16 15:19:21";"2021-11-16 15:20:11" +PM;"Peinture mate sur préparations :";"Peinture mate sur préparations :";23.00;0.00;23.00;23.60;19.64;19.00;20.00;0.00;0.00;0.00;;;;PE-END01;;;;;;;;PEI;;;;;"2017-08-15 09:18:37";"2022-10-20 08:35:15" +PMM;"Peinture mate sur murs y";"Peinture mate sur murs y compris préparations :";0.00;20.70;0.00;0.00;18.40;0.00;0.00;0.00;0.00;0.00;;PEN_2_MISEEN;;;;;;;;;;PEI;"compris préparations :";;;;"2019-06-12 11:57:10";"2022-10-20 08:36:14" +PMP;"Peinture mate sur plafond y ";"Peinture mate sur plafond y compris préparations :";0.00;22.30;0.00;0.00;19.64;0.00;0.00;0.00;0.00;0.00;;PEN_3_MISEEN;;;;;;;;;;PEI;"compris préparations :";;;;"2019-02-08 08:22:09";"2022-10-20 08:35:45" +PN;"Dépose de revêtement plastique";"Dépose de revêtement plastique moquette non collé et évacuation";4.80;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;"moquette non collé et évacuation";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PP;"Pose de papier peint sur";"Pose de papier peint sur préparations :";23.60;20.10;23.60;22.00;16.15;20.50;19.00;0.00;0.00;0.00;;PEN_7_REMPLA;;;;;;;;;;PNT;"préparations :";;;;"2017-08-15 09:18:37";"2022-10-20 15:46:35" +PPP;"Pose de papier à peindre sur";"Pose de papier à peindre sur préparations :";18.50;0.00;0.00;33.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;"préparations :";;;;"2017-08-15 09:18:37";"2018-05-17 07:46:40" +PPR;"PIECE PRINCIPALE";"PIECE PRINCIPALE ================";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;================;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PR;"Pose de revêtement de sol collé :";"Pose de revêtement de sol collé :";9.00;0.00;14.08;10.35;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PRE;"Raclage, ratissage sur parties";"Raclage, ratissage sur parties écaillées :";0.00;0.00;0.00;0.00;0.00;38.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;"écaillées :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PRO;"Protection et nettoyage :";"Protection et nettoyage :";0.00;3.58;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;PNT;;;;;"2017-08-15 09:18:37";"2020-05-07 10:53:00" +PROT;"Protection et nettoyage sur l'ensemble";"Protection et nettoyage sur l'ensemble";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +PS;"Peinture satinée ou semibrillante sur";"Peinture satinée ou semibrillante sur préparations :";23.00;0.00;23.00;23.60;19.64;20.00;20.00;0.00;0.00;0.00;;;;;;;;;;;;PEI;"préparations :";;;;"2017-08-15 09:18:37";"2022-10-20 15:44:51" +PSB;"Peinture satinée sur boiseries";"Peinture satinée sur boiseries";0.00;0.00;33.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;PEI;;;;;"2020-02-21 09:06:13";"2020-02-21 09:06:42" +PSM;"Peinture satinée sur préparations :";"Peinture satinée sur préparations :";0.00;20.70;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_2_MISEEN;;;;;;;;;;PEI;;;;;"2019-06-12 12:00:14";"2022-07-08 15:47:30" +PT;"Plafond et retombées";"Plafond et retombées --------------------";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;--------------------;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +R;"Rechampis du pourtour :";"Rechampis du pourtour :";1.50;1.50;1.50;1.50;1.50;1.50;1.50;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;0;PNT;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +RA;"Ratissage à 2 passes, ponçage :";"Ratissage à 2 passes, ponçage :";15.24;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PEI;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +RBA;"Fourniture et pose de faux plafond :";"Fourniture et pose de faux plafond :";52.00;48.00;45.50;41.40;52.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +RDC;"REZ DE CHAUSSEE";"REZ DE CHAUSSEE ===============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;===============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +RDF1;"Forfait RDF simple :";NULL;155.00;0.00;155.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;AR;;;;;"2019-06-20 16:58:30";"2022-02-07 13:48:44" +RDF2;"Forfait RDF spécialisée :";NULL;484.00;0.00;484.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;;;;;;"2019-06-20 16:58:30";"2022-02-07 13:48:33" +RE4;"Remise 4 % :";"Remise 4 % :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;T;;;;;"2020-02-04 15:28:31";"2020-02-04 15:28:56" +RE6;"Remise 6 % :";"Remise 6 % :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;T;;;;;"2020-02-04 15:28:04";"2020-02-04 15:29:21" +REP;"Raclage, ratissage sur parties";"Raclage, ratissage sur parties écaillées :";0.00;0.00;0.00;0.00;0.00;38.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;0;PLA;"écaillées :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +RL;"Ragréage, Lissage";"Ragréage, Lissage";23.00;9.60;0.00;12.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2018-08-07 14:47:27" +RO;"Fourniture et pose de laine de verre";"Fourniture et pose de laine de verre ou laine de roche :";25.00;25.00;25.00;25.00;25.00;25.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PLA;"ou laine de roche :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +RSBASE;"Fourniture et pose de barre de seuil :";NULL;0.00;0.00;0.00;14.10;0.00;0.00;0.00;0.00;0.00;0.00;;;;RS-BASEU;;;;;;;;PAR;;;;;"2022-10-03 08:59:07";"2022-10-03 08:59:57" +RSPACL;"Dépose et remplacement de parquet";NULL;0.00;0.00;0.00;71.10;0.00;0.00;0.00;0.00;0.00;0.00;;;;RS-PACLO;;;;;;;;PAR;"cloué sur lambourdes, y compris";"préparations :";;;"2022-10-03 08:57:46";"2022-10-19 16:43:26" +RSPAFL;"Dépose et remplacement de parquet";NULL;0.00;0.00;0.00;32.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;RS-PAFLO;;;;;;;;PAR;"flottant avec feutre, y compris préparation";"des supports et pose :";;;"2022-10-19 16:35:03";"2022-10-19 16:41:11" +RSPLIN;"Remplacement de plinthes bois";NULL;0.00;0.00;0.00;12.10;0.00;0.00;0.00;0.00;0.00;0.00;;;;RS-PLIN;;;;;;;;PAR;"ou PVC :";;;;"2022-10-03 08:56:39";"2022-10-03 08:57:35" +RSPVVI;"Ponçage et vitrification de parquet";NULL;0.00;0.00;0.00;35.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;RS-PVVIT;;;;;;;;PAR;"en complément au delà des 12 m² :";;;;"2022-10-03 08:54:37";"2022-10-03 08:56:21" +RSVITR;"Ponçage et vitrification de parquet";NULL;0.00;0.00;0.00;430.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;RS-VITRI;;;;;;;;PAR;"Forfait minimum < 12 m² :";;;;"2022-10-03 08:53:05";"2022-10-03 08:54:21" +S;Sol;"Sol ---";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;---;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SA;SALON;"SALON =====";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;=====;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SAM;"SALLE A MANGER";"SALLE A MANGER ==============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SC;"Application de sous couche spéciale";"Application de sous couche spéciale produit décoratif :";12.96;12.96;12.96;12.96;12.96;12.96;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;"produit décoratif :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SDB;"SALLE DE BAINS";"SALLE DE BAINS ==============";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;==============;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SE;SEJOUR;"SEJOUR ======";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;======;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SER0;"Prestation hors référentiel";"Prestation hors référentiel tarifaire :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;SER_0_HORBPU;;;;;;;;;;MEN;"tarifaire :";;;;"2020-02-25 09:50:03";"2020-02-25 09:51:27" +SER3;"Remplacement verrou ou cylindre";"Remplacement verrou ou cylindre tout type (à bouton ou double entrée y compris haute sureté, déplacement compris :";0.00;81.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;SER_3_REMPL;;;;;;;;;;MEN;"tout type (à bouton ou double entrée";"y compris haute sureté, déplacement";"compris :";;"2020-08-03 15:34:44";"2022-07-04 10:32:31" +SER4;"Remplacement serrure mono point";"Remplacement serrure mono point tout type (à larder ou en applique) déplacement compris :";0.00;102.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;SER_4_REMP;;;;;;;;;;MEN;"tout type (à larder ou en applique)";"déplacement compris :";;;"2020-08-03 15:38:33";"2022-07-04 10:32:50" +SLS024;"Fourniture et pose de lés vinyles";NULL;0.00;0.00;0.00;0.00;0.00;0.00;21.23;0.00;0.00;0.00;;;;;;;SLS024;;;;;SOL;"sur sol déposé :";;;;"2021-11-30 17:30:36";"2021-11-30 17:31:57" +SM;"Shampooing de moquette :";"Shampooing de moquette :";10.00;10.00;10.00;7.87;10.00;7.40;11.50;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;SOL;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +SOL;"Dépose du revêtement de sol existant,";"Dépose du revêtement de sol existant, préparations des fonds et pose de revêtement collé :";42.10;18.80;42.10;22.00;21.69;21.00;25.50;0.00;0.00;0.00;;PEN_13_MOQU;;RS-MOPVC;;;;;;;;SOL;"préparations des fonds et pose de revêtement";"collé :";;;"2017-08-15 09:18:37";"2022-10-19 16:38:41" +TCF;"Ce devis ne comprend pas les dommages";NULL;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;PEI;"en Plâtrerie Peinture occasionnés par";"la réparation de la fuite.";;;"2022-10-10 11:15:09";"2022-10-10 11:17:36" +TD;"Traitement et valorisation des déchets";"Traitement et valorisation des déchets montant forfaitaire de 1 % :";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;GEN_0_DECHET;;;;;;;;;;T;"montant forfaitaire de 1 % :";;;;"2020-02-04 08:23:31";"2022-02-08 16:20:49" +TT;"Pose de tissu tendu sur préparations :";"Pose de tissu tendu sur préparations :";38.00;23.87;34.03;42.30;38.00;38.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PP;;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +TV;"Fourniture et pose de toile de verre";"Fourniture et pose de toile de verre sur préparations :";23.50;8.18;10.40;0.00;23.50;0.00;20.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;PNT;"sur préparations :";;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" +TVP;"Fourniture et pose de toile de verre ";"Fourniture et pose de toile de verre y compris peinture acrylique mate ou satinée :";41.40;33.20;41.40;36.00;33.15;30.00;39.00;0.00;0.00;0.00;;PEN_6_REMPLA;;;;;;;;;;PNT;"standard y compris peinture acrylique ";"mate ou satinée :";;;"2017-08-15 09:18:37";"2022-10-20 08:36:47" +TVPS;"Fourniture et pose de toile de verre y";"Fourniture et pose de toile de verre y compris peinture acrylique satinée :";40.50;32.81;33.00;33.48;40.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;PNT;"compris peinture acrylique satinée :";;;;"2017-08-15 09:18:37";"2020-02-04 08:30:50" +U;"Mise en peinture d'uniformisation :";"Mise en peinture d'uniformisation :";15.50;12.00;15.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;PEN_8_MISEEN;;;;;;;;;;PEI;;;;;"2019-06-12 12:03:28";"2022-02-07 09:16:29" +URG10;"Fermeture provisoire de porte 1 vantail";NULL;0.00;270.50;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;URG_10_FERM;;;;;;;;;;MEN;"avec du contre-plaqué rigide ou";"renforcé par des tasseaux de bois :";;;"2022-09-20 16:28:16";"2022-09-29 11:48:21" +URG3;"Remplacement simple vitrage";"Remplacement simple vitrage clair, d'épaisseur < à 10 mm et de surface < à 1 m², y compris déplacements et traitement des déchets :";0.00;179.20;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;URG_3_REMPL;;;;;;;;;;MEN;"clair, d'épaisseur < à 10 mm et";"de surface < à 1 m², y compris";"déplacements et traitement des";"déchets :";"2020-02-25 09:51:46";"2022-07-04 10:33:13" +VEN;"Pose d'un ventilateur :";"Pose d'un ventilateur :";15.00;0.00;15.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;;;;;;;;;;;;ASS;;;;;"2020-01-15 15:54:15";"2021-10-27 09:09:41" +WC;W.C.;"W.C. ====";0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;0.00;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;NULL;;T;====;;;;"2017-08-15 09:18:37";"2017-08-15 09:18:37" diff --git a/cao_osint/static/uploads/dolibarr-psw.txt b/cao_osint/static/uploads/dolibarr-psw.txt new file mode 100644 index 0000000..a83a38c --- /dev/null +++ b/cao_osint/static/uploads/dolibarr-psw.txt @@ -0,0 +1,6 @@ +https://caotekfr.with.multicompany.cloud/?username=admin +admin - pcao.8211 + +http://dolibarr.driveperso.com/index.php +caotek - q4txk0mr + diff --git a/cao_osint/templates/404.jinja2 b/cao_osint/templates/404.jinja2 new file mode 100644 index 0000000..aaf1241 --- /dev/null +++ b/cao_osint/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Starter project

+

404 Page Not Found

+
+{% endblock content %} diff --git a/cao_osint/templates/apropos.jinja2 b/cao_osint/templates/apropos.jinja2 new file mode 100644 index 0000000..bc08d18 --- /dev/null +++ b/cao_osint/templates/apropos.jinja2 @@ -0,0 +1,33 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+
+
+
+
+
+ L'argent qu'on possède est l'instrument de la liberté; celui qu'on pourchasse est celui de la servitude. +
+
+ +
+
+
+
+
+
+ L'intelligence ce n'est pas ce que l'on sait mais ce que l'on fait quand on ne sait pas. +
+
+
+ +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/cao_osint/templates/blog.jinja2 b/cao_osint/templates/blog.jinja2 new file mode 100644 index 0000000..c05b061 --- /dev/null +++ b/cao_osint/templates/blog.jinja2 @@ -0,0 +1,34 @@ +{% extends "cao_osint:templates/layout.jinja2" %} + +{% block content %} + {% if request.authenticated_userid %} +

+ [ Retour ] + [ Modifier ] + +

+ {% endif %} + +
+

{{ body_html | safe }}

+
+

+ Auteur : {{ entry.author }}
+ Publié le : {{ entry.created.strftime("%d-%m-%Y - %H:%M") }}
+ {% if request.authenticated_userid %} + Modifié le : {{ entry.edited.strftime("%d-%m-%Y - %H:%M") }}
+ Tag : {{ entry.tag }}
+ Statut : {{ entry.status }} + {% endif %} +

+ + + +{% endblock %} + diff --git a/cao_osint/templates/blog_edit.jinja2 b/cao_osint/templates/blog_edit.jinja2 new file mode 100644 index 0000000..504e2dd --- /dev/null +++ b/cao_osint/templates/blog_edit.jinja2 @@ -0,0 +1,80 @@ +{% extends "cao_osint:templates/layout.jinja2" %} + +{% block content %} +
+ + {% for error in form.title.errors %} +
{{ error }}
+ {% endfor %} +
+ + {{ form.title(class_='form-control') }} +
+ + {% for error in form.body.errors %} +
{{ error }}
+ {% endfor %} +
+ + {{ form.body(class_='form-control', cols="35", rows="20") }} +
+ +
+ + {{ form.tag(class_='form-control') }} +
+ +
+ + {{ form.status(class_='form-control') }} +
+ +

+ {% if blog_id != '0' %} + Créé le : {{ entry.created.strftime("%d-%m-%Y - %H:%M") }}
+ Modifié le : {{ entry.edited.strftime("%d-%m-%Y - %H:%M") }} + {% endif %} +

+ +
+
+ Retour + + {% if blog_id != '0' %} + + {% endif %} +
+ +

Apprendre la syntaxe de Markdown

+ +
+ + + + + {% endblock %} diff --git a/cao_osint/templates/blog_search.jinja2 b/cao_osint/templates/blog_search.jinja2 new file mode 100644 index 0000000..ef4fd43 --- /dev/null +++ b/cao_osint/templates/blog_search.jinja2 @@ -0,0 +1,51 @@ +{% extends "cao_osint:templates/layout.jinja2" %} + +{% block content %} +
+ +
+
+
+ {{ form.criteria(class_='form-control') }} + + + +
+ {% for error in form.criteria.errors %} +
{{ error }}
+ {% endfor %} +
+
+
+ +
+ {% if items %} + + + + + + + + + + {% for entry in items %} + + + + + + {% endfor %} +
TitreTagsDate
+ + {{ entry.title }} + + {{ entry.tag }}{{ entry.edited.strftime("%d-%m-%Y - %H:%M") }}
+ {% endif %} +
+
+
+ +{% endblock %} diff --git a/cao_osint/templates/home.jinja2 b/cao_osint/templates/home.jinja2 new file mode 100644 index 0000000..50c91c5 --- /dev/null +++ b/cao_osint/templates/home.jinja2 @@ -0,0 +1,54 @@ +{% extends "layout.jinja2" %} + +{% block content %} + + {% if request.authenticated_userid %} +

+ Nouveau +

+ {% endif%} + + + {% for entry in last_ten %} + + + + + {% if entry.status != 'publié' %} + + {% else %} + + {% endif%} + + {% else %} +

Aucun post trouvé

+ {% endfor %} +
{{ entry.created.strftime("%d.%m.%Y") }} + {{ entry.title }} + {{ entry.tag }}{{ entry.status }} 
+ + +
+
+
+
+ +
+
+ +
+ {{ form.criteria(class_='form-control') }} + + + +
+ {% for error in form.criteria.errors %} +
{{ error }}
+ {% endfor %} +
+
+
+ +{% endblock %} diff --git a/cao_osint/templates/layout.jinja2 b/cao_osint/templates/layout.jinja2 new file mode 100644 index 0000000..8d86293 --- /dev/null +++ b/cao_osint/templates/layout.jinja2 @@ -0,0 +1,114 @@ + + + + + + + + + + {{page_title}} + + + + + + + + + + + + + + + +{% if request.path == '/' %} + {# -- display carousel -- #} + {% block carousel %} + {% endblock carousel %} +{% endif %} + + +
+ + {% if page_title %} +

{{ page_title }}

+ {% endif %} +
+
+ {% for queue in ['', 'info', 'success', 'warning', 'danger'] %} + {% for message in request.session.pop_flash(queue) %} +
+ + {{ message }} +
+ {% endfor %} + {% endfor %} +
+ + + {% block content %} +

No content

+ {% endblock content %} +
+
+
+ + + + + + + + + + diff --git a/cao_osint/templates/login.jinja2 b/cao_osint/templates/login.jinja2 new file mode 100644 index 0000000..704dc22 --- /dev/null +++ b/cao_osint/templates/login.jinja2 @@ -0,0 +1,28 @@ +{% extends "layout.jinja2" %} + +{% block content %} + +
+
+ +
+

Se connecter

+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ +{% endblock %} diff --git a/cao_osint/templates/tag_edit.jinja2 b/cao_osint/templates/tag_edit.jinja2 new file mode 100644 index 0000000..c5db2bc --- /dev/null +++ b/cao_osint/templates/tag_edit.jinja2 @@ -0,0 +1,55 @@ +{% extends "cao_osint:templates/layout.jinja2" %} + +{% block content %} + +
+ + {% for error in form.tag.errors %} +
{{ error }}
+ {% endfor %} + +
+ + {{form.tag(class_='form-control')}} +
+ +
+ + Retour + + {% if form.id.data %} + + {% endif %} +
+ + +
+ + + + +{% endblock %} diff --git a/cao_osint/templates/tags.jinja2 b/cao_osint/templates/tags.jinja2 new file mode 100644 index 0000000..b403058 --- /dev/null +++ b/cao_osint/templates/tags.jinja2 @@ -0,0 +1,26 @@ +{% extends "cao_osint:templates/layout.jinja2" %} + +{% block content %} + +

+ Nouveau +

+ + + + + + + + + {% for entry in tags %} + + + + + {% endfor %} +
No IdTag
{{ entry.id }} + {{ entry.tag }} +
+ +{% endblock %} diff --git a/cao_osint/templates/upload_edit.jinja2 b/cao_osint/templates/upload_edit.jinja2 new file mode 100644 index 0000000..f7edfaf --- /dev/null +++ b/cao_osint/templates/upload_edit.jinja2 @@ -0,0 +1,52 @@ +{% extends "layout.jinja2" %} + +{% block content %} + {% if message %} + {{ message }} + {% endif %} + + +
+ +
+ + +
+
+
+ Retour + + +
+ +
+ + + + +{% endblock %} diff --git a/cao_osint/templates/uploads.jinja2 b/cao_osint/templates/uploads.jinja2 new file mode 100644 index 0000000..b82da92 --- /dev/null +++ b/cao_osint/templates/uploads.jinja2 @@ -0,0 +1,43 @@ +{% extends "cao_osint:templates/layout.jinja2" %} +{% block content %} + +
+
+
Ajouter un fichier
+
+
+
+ + + + + +
+
+
+
    +
  • Seuls les documents au format TXT, PDF, PNG, JPG ou JPEG seront acceptés.
  • +
  • La taille de chaque document ne doit pas dépasser 4 Mo.
  • +
+
+
+
+
+
Fichiers téléchargés
+
+ + {% for entry in files_list %} + + + + + + {% endfor %} +
{{ entry[0] }}{{ entry[1] }}[ Modifier ]
+
+
+
+ + +{% endblock %} diff --git a/cao_osint/templates/user_edit.jinja2 b/cao_osint/templates/user_edit.jinja2 new file mode 100644 index 0000000..cb27aa0 --- /dev/null +++ b/cao_osint/templates/user_edit.jinja2 @@ -0,0 +1,55 @@ +{% extends "cao_osint:templates/layout.jinja2" %} + +{% block content %} + + {% if message %} +
+ {{ message }} +
+ {% endif %} + +
+ + {% for error in form.name.errors %} +
{{ error }}
+ {% endfor %} + +
+ + {% if form.id.data %} + + {% else %} + {{form.name(class_='form-control')}} + {% endif %} +
+ +
+ + {{form.password(class_='form-control')}} +
+ + {% for error in form.confirm.errors %} +
{{error}}
+ {% endfor %} + +
+ + {{form.confirm(class_='form-control')}} +
+ +
+
+ Retour + + {% if form.id.data and request.authenticated_userid == 'admin' %} + + {% endif %} + +
+ + +
+ +{% endblock %} diff --git a/cao_osint/templates/users.jinja2 b/cao_osint/templates/users.jinja2 new file mode 100644 index 0000000..46d697c --- /dev/null +++ b/cao_osint/templates/users.jinja2 @@ -0,0 +1,32 @@ +{% extends "layout.jinja2" %} + +{% block content %} +

+ + Retour + + Nouvel utilisateur +

+ + + + + + + + + + {% for entry in users %} + + + + + + {% endfor %} +
No IdNomDernière connexion
{{ entry.id }} + + {{ entry.name }} + + {{ entry.last_logged.strftime("%d-%m-%Y - %H:%M") }}
+ +{% endblock %} diff --git a/cao_osint/tests.py b/cao_osint/tests.py new file mode 100644 index 0000000..fe46681 --- /dev/null +++ b/cao_osint/tests.py @@ -0,0 +1,56 @@ +import unittest + +from pyramid import testing + +import transaction + + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): + def setUp(self): + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models') + settings = self.config.get_settings() + + from .models import ( + get_engine, + get_session_factory, + get_tm_session, + ) + + self.engine = get_engine(settings) + session_factory = get_session_factory(self.engine) + + self.session = get_tm_session(session_factory, transaction.manager) + + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) + + def tearDown(self): + from .models.meta import Base + + testing.tearDown() + transaction.abort() + Base.metadata.drop_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): + + def test_passing_view(self): + from .views.default import apropos + response = apropos(dummy_request(self.session)) + self.assertEqual(response['page_title'], 'A propos') + + +class TestMyViewFailureCondition(BaseTest): + + def test_failing_view(self): + from .views.default import apropos + response = apropos(dummy_request(self.session)) + self.assertEqual(response['page_title'], 'A propos') diff --git a/cao_osint/views/__init__.py b/cao_osint/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cao_osint/views/blog.py b/cao_osint/views/blog.py new file mode 100644 index 0000000..e3d0d08 --- /dev/null +++ b/cao_osint/views/blog.py @@ -0,0 +1,160 @@ +from pyramid.view import view_config +from pyramid.httpexceptions import HTTPNotFound, HTTPFound +from ..models.blog_record import BlogRecord, Tags +from ..services.blog_record import BlogRecordService +from ..forms import BlogCreateForm, BlogUpdateForm, BlogSearchForm, TagForm +import markdown +import datetime #<- will be used to set default dates on models + +@view_config(route_name='blog', + renderer='cao_osint:templates/blog.jinja2') +def blog(request): + # get post id from request + blog_id = request.matchdict['id'] + entry = BlogRecordService.by_id(request, blog_id) + + if not entry: + request.session.flash(u"Page non trouvée : %s" % blog_id, 'warning') + return HTTPFound(location=request.route_url('home')) + + # insèrer le path de static/img + body = entry.body.replace('static/', "%s/static/" % request.application_url) + # convertir de markdown en HTML + body_html = markdown.markdown(body, extensions=['footnotes']) + + return { + 'page_title': entry.title, + 'entry': entry, + 'body_html': body_html, + } + + +@view_config(route_name='blog_edit', renderer='cao_osint:templates/blog_edit.jinja2', permission='view') +def blog_edit(request): + # get post id from request + blog_id = request.matchdict['id'] + url = request.route_url('blog_edit',id=blog_id) + + # get the list of tags + tags = BlogRecordService.get_tags(request) + + if blog_id == '0': + # create a new post + entry = BlogRecord() + # set default values + form = BlogCreateForm(request.POST, entry) + form.tag.choices = [(row.tag, row.tag) for row in tags] + page_title = 'Nouvelle page' + else: + # modify post + entry = BlogRecordService.by_id(request, blog_id) + if not entry: + request.session.flash(u"Page non trouvée : %s" % blog_id, 'warning') + return HTTPFound(location=request.route_url('home')) + form = BlogUpdateForm(request.POST, entry) + form.tag.choices = [(row.tag, row.tag) for row in tags] + page_title = 'Modifier : ' + entry.title + + if 'form.submitted' in request.params and form.validate(): + if blog_id == '0': + form.populate_obj(entry) + # interdire le car '/' dans le titre à cause du slug + entry.title = entry.title.replace('/','.') + entry.creator = request.authenticated_userid + entry.editor = entry.creator + request.dbsession.add(entry) + + return HTTPFound(location=request.route_url('home')) + else: + del form.id # SECURITY: prevent overwriting of primary key + form.populate_obj(entry) + # interdire le car '/' dans le titre à cause du slug + entry.title = entry.title.replace('/','.') + entry.edited = datetime.datetime.now() + entry.editor = request.authenticated_userid + return HTTPFound(location=request.route_url('blog', id=entry.id, slug=entry.slug)) + + if 'form.deleted' in request.params: + BlogRecordService.delete(request, blog_id) + request.session.flash("La page a été supprimée avec succès.", 'success') + return HTTPFound(location=request.route_url('home')) + + return { + 'page_title': page_title, + 'url': url, + 'form': form, + 'blog_id': blog_id, + 'entry': entry, } + + +@view_config(route_name='blog_search', + renderer='cao_osint:templates/blog_search.jinja2') +def blog_search(request): + + criteria = '' + items = [] + form = BlogSearchForm(request.POST) + if 'form.submitted' in request.params and form.validate(): + criteria = request.params['criteria'] + # si afficher tous les fiches ? + items = BlogRecordService.by_criteria(request, criteria) + + return { + 'page_title': "Rechercher", + 'form': form, + 'items': items, + 'criteria': criteria, + } + +@view_config(route_name='tags', renderer='cao_osint:templates/tags.jinja2', permission='view') +def tags(request): + + # get the list of tags of this topic + tags = BlogRecordService.get_tags(request) + + return { + 'page_title': 'Tags', + 'tags': tags, + } + +@view_config(route_name='tag_edit', renderer='cao_osint:templates/tag_edit.jinja2', permission='view') +def tag_edit(request): + # get tag parameters from request + tag_id = request.matchdict['id'] + url = request.route_url('tag_edit', id=tag_id) + + if tag_id == '0': + # create a new tag + entry = Tags() + form = TagForm(request.POST, entry) + page_title = "Nouveau Tag" + + else: + # modify post + entry = BlogRecordService.get_tags_byId(request, tag_id) + if not entry: + request.session.flash(u"Tag non trouvé : %s" % tag_id, 'warning') + return HTTPFound(location=request.route_url('tags')) + form = TagForm(request.POST, entry) + page_title = entry.tag + + if 'form.submitted' in request.params and form.validate(): + if tag_id == '0': + form.populate_obj(entry) + request.dbsession.add(entry) + return HTTPFound(location=request.route_url('tags')) + else: + del form.id # SECURITY: prevent overwriting of primary key + form.populate_obj(entry) + return HTTPFound(location=request.route_url('tags')) + + if 'form.deleted' in request.params: + BlogRecordService.tag_delete(request, entry.id) + request.session.flash("La fiche a été supprimée avec succès.", 'success') + return HTTPFound(location=request.route_url('tags')) + + return { + 'page_title': page_title, + 'url': url, + 'form': form, + } diff --git a/cao_osint/views/default.py b/cao_osint/views/default.py new file mode 100644 index 0000000..aa51809 --- /dev/null +++ b/cao_osint/views/default.py @@ -0,0 +1,245 @@ +from pyramid.view import ( + view_config, + forbidden_view_config, +) +from pyramid.httpexceptions import HTTPFound +from pyramid.security import remember, forget +from ..services.user import UserService +from ..services.blog_record import BlogRecordService +from ..forms import UserCreateForm, BlogSearchForm +from ..models.user import User + +import os +import shutil +import magic + +@view_config(route_name='home', + renderer='cao_osint:templates/home.jinja2') +def home(request): + # get the last created posts + last_ten = BlogRecordService.get_last_created(request) + + criteria = '' + form = BlogSearchForm(request.POST) + + return { + 'page_title': "Bienvenue sur mon blog", + 'last_ten': last_ten, + 'form': form, + 'criteria': criteria, + } + + +@view_config(route_name='apropos', + renderer='cao_osint:templates/apropos.jinja2') +def apropos(request): + + return { + 'page_title': "A propos", + } + + +@view_config(route_name='login', renderer='cao_osint:templates/login.jinja2') +@forbidden_view_config(renderer='cao_osint:templates/login.jinja2') +def login(request): + username = '' + login_url = request.route_url('login') + + referrer = request.url + if referrer == login_url: + referrer = '/' # never use the login form itself as came_from + + came_from = request.params.get('came_from', referrer) + username = request.POST.get('username') + userpwd = request.POST.get('password') + if username: + user = UserService.by_name(request, username) + if user and user.verify_password(userpwd): + headers = remember(request, username) + request.session.flash("Bienvenue %s !" % username, 'success') + return HTTPFound(location=came_from, headers=headers) + else: + headers = forget(request) + request.session.flash("Login et mot de passe invalides. La connexion a échoué.", "danger") + + return { + 'page_title': "", + 'came_from': came_from, + 'login_url': login_url, + } + + +@view_config(route_name='logout', renderer='string') +def logout(request): + username = request.authenticated_userid + headers = forget(request) + request.session.flash('Au revoir ' + username + ' !', 'success') + return HTTPFound(location=request.route_url('home'), headers=headers) + + +@view_config(route_name='users', renderer='cao_osint:templates/users.jinja2', permission='manage') +def users(request): + # get all users + users = UserService.all(request) + return { + 'page_title': "Liste des utilisateurs", + 'users': users + } + + +@view_config(route_name='user_edit', renderer='cao_osint:templates/user_edit.jinja2', permission='view') +def user_edit(request): + message = '' + name = request.matchdict['name'] + url = request.route_url('user_edit', name=name) + if request.authenticated_userid == 'admin': + url_retour = request.route_url('users') + else: + url_retour = request.route_url('home') + + if name == '0': + # nouvel utilisateur + user = User() + form = UserCreateForm(request.POST, user) + page_title = "Nouvel utilisateur" + else: + # lire la fiche du user + user = UserService.by_name(request, name) + if not user: + request.session.flash("Utilisateur non trouvé : %s" % name, 'danger') + return HTTPFound(location=url_retour) + + form = UserCreateForm(request.POST, user) + page_title = "Modification utilisateur" + + + if 'form.submitted' in request.params and form.validate(): + # controle que le password a moins 6 car + if len(form.password.data) < 6 : + message = "Le mot de passe doit avoir au moins 6 caractères" + else: + if name == '0': + # en création, controler que le nouvel user n'existe pas dans la BD + new_user = UserService.by_name(request, form.name.data) + if new_user: + message = "Utilisateur déjà créé : %s" % form.name.data + else: + form.populate_obj(user) + user.set_password(form.password.data.encode('utf8')) + # créer le nouveau + request.dbsession.add(user) + request.session.flash("La fiche a été créée avec succès.", 'success') + return HTTPFound(location=url_retour) + + else: + # en modif + del form.name # SECURITY: prevent overwriting of primary key + form.populate_obj(user) + user.set_password(form.password.data.encode('utf8')) + request.session.flash("La fiche a été modifiée avec succès.", 'success') + return HTTPFound(location=url_retour) + + if 'form.deleted' in request.params: + UserService.delete(request, user.id) + request.session.flash("La fiche a été supprimée avec succès.", 'success') + return HTTPFound(location=url_retour) + + return { + 'page_title': page_title, + 'message': message, + 'form': form, + 'url': url, + 'url_retour': url_retour, + 'name': name, + } + + +@view_config(route_name='uploads', renderer='cao_osint:templates/uploads.jinja2', permission='view') +def uploads(request): + message = '' + folder_path = request.registry.settings['uploads_dir'] + + files_list = [] + # récupérer tous les fichiers contenus dans dossier static/uploads + for f in os.scandir(folder_path): + file = [] + file_url = request.static_url('cao_osint:static/uploads/') + f.name + file_path = folder_path + f.name + file.append(f.name) + file.append('%s Ko' % round(os.path.getsize(file_path) / 1024)) + file.append(file_url) + files_list.append(file) + + + if 'form.submitted' in request.params: + # controler que le nom du fichier n'est pas vide + if request.POST['uploadfile'] != b'': + # récupère le fichier download dans le dossier /tmp + input_file = request.POST['uploadfile'].file + input_name = request.POST['uploadfile'].filename + ext_allowed = ['text/plain', 'application/pdf', 'image/jpeg', 'image/jpg', 'image/png'] + + mime = magic.from_buffer(input_file.read(), mime=True) + import pdb;pdb.set_trace() + # types de fichiers autorisés ? + if mime not in ext_allowed: + request.session.flash("ERREUR: Le format du fichier n'est pas valide. Téléchargement impossible.", 'danger') + return HTTPFound(location=request.route_url('uploads')) + + # Finally write the data to a temporary file + temp_file_path = os.path.join(folder_path, input_name) + # supprimer le fichier s'il existe déjà + if os.path.exists(temp_file_path): + os.remove(temp_file_path) + + # copie le fichier upload dans temp_file + input_file.seek(0) + with open(temp_file_path, 'wb') as output_file: + shutil.copyfileobj(input_file, output_file) + + # controler la taille du fichier < 4 Mo + filesize = round(os.path.getsize(temp_file_path) / 1024) + if filesize > 4096: + os.remove(temp_file_path) + request.session.flash("ERREUR: La taille du fichier dépasse la limite autorisée. Téléchargement impossible.", 'danger') + return HTTPFound(location=request.route_url('uploads')) + + request.session.flash('%s : Ce fichier est téléchargé avec succès.' % input_name, 'success') + return HTTPFound(location=request.route_url('uploads')) + + + return { + 'page_title': "Téléchargements", + 'files_list': files_list + } + + +@view_config(route_name='upload_edit', renderer='cao_osint:templates/upload_edit.jinja2') +def upload_edit(request): + filename = request.matchdict['filename'] + + message = '' + folder_path = request.registry.settings['uploads_dir'] + + if 'form.submitted' in request.params: + new_name = request.params["new_name"].lower() + ext = new_name[-3:] + # comparer le nouveau ext avec l'ancien + if ext == filename[-3:]: + os.rename(folder_path + filename, folder_path + new_name) + return HTTPFound(location=request.route_url('uploads')) + else: + message = "Extension de fichier invalide !" + + if 'form.deleted' in request.params: + os.remove(folder_path + filename) + request.session.flash("Le fichier a été supprimé avec succès.", 'success') + return HTTPFound(location=request.route_url('uploads')) + + return { + 'page_title': "Modifier : " + filename, + 'message': message, + 'filename': filename, + 'file_url': request.static_url('cao_osint:static/uploads/') + filename, + } + diff --git a/cao_osint/views/notfound.py b/cao_osint/views/notfound.py new file mode 100644 index 0000000..69d6e28 --- /dev/null +++ b/cao_osint/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/development.ini b/development.ini new file mode 100644 index 0000000..42e2741 --- /dev/null +++ b/development.ini @@ -0,0 +1,83 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:cao_osint + +pyramid.reload_templates = true +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = + pyramid_debugtoolbar + +sqlalchemy.url = sqlite:///%(here)s/cao_osint.sqlite + +# upload files location +uploads_dir = /pyramid/cao_osint/cao_osint/static/uploads/ + +retry.attempts = 3 + +# By default, the toolbar only appears for clients from IP addresses +# '127.0.0.1' and '::1'. +# debugtoolbar.hosts = 127.0.0.1 ::1 + +[pshell] +setup = cao_osint.pshell.setup + +### +# wsgi server configuration +### + +[alembic] +# path to migration scripts +script_location = cao_osint/alembic +file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s +# file_template = %%(rev)s_%%(slug)s + +[server:main] +use = egg:waitress#main +listen = localhost:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, cao_osint, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_cao_osint] +level = DEBUG +handlers = +qualname = cao_osint + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/production.ini b/production.ini new file mode 100644 index 0000000..f86905e --- /dev/null +++ b/production.ini @@ -0,0 +1,76 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:cao_osint + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +sqlalchemy.url = sqlite:///%(here)s/cao_osint.sqlite +# upload files location +uploads_dir = /pyramid/cao_osint/cao_osint/static/uploads/ + +retry.attempts = 3 + +[pshell] +setup = cao_osint.pshell.setup + +### +# wsgi server configuration +### + +[alembic] +# path to migration scripts +script_location = cao_osint/alembic +file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s +# file_template = %%(rev)s_%%(slug)s + +[server:main] +use = egg:waitress#main +listen = *:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, cao_osint, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_cao_osint] +level = WARN +handlers = +qualname = cao_osint + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..d084c69 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +testpaths = cao_osint +python_files = test*.py diff --git a/rtd.txt b/rtd.txt new file mode 100644 index 0000000..93b76e4 --- /dev/null +++ b/rtd.txt @@ -0,0 +1 @@ +pylons-sphinx-themes diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..dea6ea6 --- /dev/null +++ b/setup.py @@ -0,0 +1,67 @@ +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README.md')) as f: + README = f.read() +with open(os.path.join(here, 'CHANGES.txt')) as f: + CHANGES = f.read() + +requires = [ + 'plaster_pastedeploy', + 'pyramid', + 'pyramid_jinja2', + 'pyramid_debugtoolbar', + 'waitress', + 'alembic', + 'pyramid_retry', + 'pyramid_layout', + 'pyramid_tm', + 'python-magic', + 'SQLAlchemy', + 'transaction', + 'zope.sqlalchemy', + 'wtforms', # form library 2.2.1 + 'webhelpers2', # various web building related helpers 2.0 + 'passlib', + 'markdown', +] + +tests_require = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest >= 3.7.4', + 'pytest-cov', +] + +setup( + name='cao_osint', + version='1.0', + description='cao_osint', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + 'Programming Language :: Python', + 'Framework :: Pyramid', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], + author='', + author_email='', + url='', + keywords='web pyramid pylons', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + extras_require={ + 'testing': tests_require, + }, + install_requires=requires, + entry_points={ + 'paste.app_factory': [ + 'main = cao_osint:main', + ], + 'console_scripts': [ + 'initialize_cao_osint_db=cao_osint.scripts.initialize_db:main', + ], + }, +)