Python powered spreadsheets

Overview

Marmir is powerful and fun

Marmir takes Python data structures and turns them into spreadsheets.

It is xlwt and google spreadsheets on steroids.

It also supports: input from Django models; taking Psycopg cursors; writing out ascii tables (like psql does); and will soon support HTML tables as output. The goal is to make it easy to generate many types of useful table files with the least amount of configuration.

Marmir melts in your mouth

Installing:

$ pip install Marmir

Talk about simple to use, wow. Marmir is just this:

import datetime
import mm

now = datetime.datetime.now().replace(microsecond=0)

my_data = [ 
    {
        'msg': "My first Row",
        'id': 1,
        'when': now,
    },
    {
        'msg': "My second Row",
        'id': 2,
        'when': now,
    },

]

mm_doc = mm.Document(my_data)
mm_doc.write("example.xls")

Same example above as lists (also node the 'order' argument works in the above as well:

my_headers = ('id', 'msg', 'when')
my_data = (
    (1, "My First Row", now),
    (2, "My Second Row", now)   
)

mm_doc = mm.Document(my_data, order=my_headers)
mm_doc.write("example.xls")

Or you can get fancier:

import datetime
import mm

my_data = [ 
    {
        'msg': "My first Row",
        'id': 1,
        'when': mm.Date(datetime.datetime.now(), "%Y-%m-%dT%H:%M:%S"),
        'homepage': mm.URL("https://github.com/brianray")
    },
    {
        'msg': "My second Row",
        'id': 2,
        'when': datetime.datetime.now(),
        'homepage': mm.URL("http://twitter.com/brianray", "Tweet Me")
    },

]

mm_doc = mm.Document(my_data)
mm_doc.write("example.xls")

# also you can publish to google spreadsheats
mm_doc.write_gdata("Example Spreadsheet", "Username", "Pass")

Now for a little Django (https://www.djangoproject.com/) example:

from yourproject.models import TestModel
from mm.contrib.django.data_model import DjangoDataModel
from mm.contrib.django.grid import DjangoGrid

django_query_set = TestModel.objects.all()
mm_doc = mm.Document(django_query_set, 
              data_model_class=DjangoDataModel, 
              grid_class=DjangoGrid)
mm_doc.write("django_example.xls")

There is a lot more. Check out the Examples.

... Not in your hand

So the primary goals are:

  • make XLS spreadsheets better than xlwt
  • Create Spreadsheets in Google Docs
  • convert python types automagically, date is a date, int is a int, string is a string, ...
  • do stuff you expect like make columns wider to fit, wrap in some cases
  • make stuff pretty colors and easier to read
  • generate directly from Django queries

Some other stuff:

  • do summaries and break out tables
  • add logic and math functions

Marmir is written with love

Brian Ray @brianray wrote Marmir. Brian: is the organizer of ChiPy (http://chipy.org); one of Chicago's Top Tech 25 (according to Crains Oct 2011); been professionally developing software for business for 15 years; and is sick of sub-par Python libraries for creating business spreadsheets.

The name Marmir name from parts of the names Maura and Miranda, the author's girls.

Copyright

Copyright (c) 2013 Brian Ray

Comments
  • object of type 'NoneType' has no len()

    object of type 'NoneType' has no len()

    I got that error when I'm trying to create xls. My code is something like this:

    from mymodel.models import MyList from mm.contrib.django.data_model import DjangoDataModel from mm.contrib.django.grid import DjangoGrid import mm

    thelist = MyList.objects.filter(create_time__range=(start,end)).extra(select={ 'newfield': 'select name from newtable where id=thistable.people_id'}).order_by('-create_time')

    mm_doc = mm.Document(thelist, data_model_class=DjangoDataModel, grid_class=DjangoGrid) mm_doc.write('example.xls')

    I'm using Django 1.4

    The same thing happen when I iterate the queryset and build the list of dict manually.

    opened by lantip 4
  • Doing more with less

    Doing more with less

    Brian; would really love to use this. I had been "rolling my own" Excel output for a Django project before coming across your project. I had been wondering how to incorporate decent formatting to the data and your project seems perfect for that.

    One small (?) issue though. The bulk of data I output from my views is in the form of (a) a list of headers and (b) a "list of lists" for data. This format is perfect for easy conversion to HTML tables, CSV data or Excel files. However your examples are dictionary-based and include header names encapsulated in each "row". Do you see a way to upgrade your project to handle the kind of input I would like to have? Thanks!

    opened by gamesbook 4
  • pip install error

    pip install error

    When I tried to install Marmir with pip it failed. Attached is a screenshot of the error log I received. Note, I haven't had any issues downloading anything else with pip. I also used pip to install another package right after I tried to install this one, and I didn't have any issues.

    error_log

    opened by tetter36 1
  • Proposal to generate files in tests/generated_files subdirectory

    Proposal to generate files in tests/generated_files subdirectory

    The generated XLS files are currently dumped into the project root directory. Can we output those files into tests/generated_files subdirectory instead?

    opened by pebreo 1
  • Proposal to change syntax of how files are opened in test suites

    Proposal to change syntax of how files are opened in test suites

    I propose to change the syntax of all unittests that currently open files like this:

    f = open("foo.xls", "wb")
    f.write(str)
    f.close()
    

    to the more idiomatic:

    with open("foo.xls", "wb") as f:
        f.write(str)
    
    opened by pebreo 1
  • Corrupt file generated using write() method in Windows

    Corrupt file generated using write() method in Windows

    The write() method generates a corrupt file that MS Excel (running in Windows environment) cannot read.

    When opening the file Excel gives the following error:

    "Excel found unreadable content in 'myfile.xls'. Do you want to recover the contents of this workbook?"

    Given the following code:

    my_data = [
                {
                    'msg': "My first Cell",
                    'id': 1,
                    'when': 'now',
                },
                {
                    'msg': "My second Cell",
                    'id': 2,
                    'when': 'now',
                },
    ]
    
    mm_doc = mm.Document(my_data)
    mm_doc.write("myfile.xls") # generates a corrupt file
    
    opened by pebreo 1
  • object of type 'NoneType' has no len()

    object of type 'NoneType' has no len()

    There was already a ticket regarding this issue... It only occurs if I export 2 or more rows at once...

    You can find the model here: https://gist.github.com/SachaMPS/e8448ffede30ed4f204c

    Heres the traceback:

    ERROR:django.request:Internal Server Error: /admin/contact/orderitem/ Traceback (most recent call last): File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/core/handlers/base.py", line 111, in get_response response = callback(request, _callback_args, *_callback_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/contrib/admin/options.py", line 366, in wrapper return self.admin_site.admin_view(view)(_args, *_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/utils/decorators.py", line 91, in _wrapped_view response = view_func(request, _args, *_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/views/decorators/cache.py", line 89, in _wrapped_view_func response = view_func(request, _args, *_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/contrib/admin/sites.py", line 196, in inner return view(request, _args, *_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/utils/decorators.py", line 25, in _wrapper return bound_func(_args, *_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/utils/decorators.py", line 91, in _wrapped_view response = view_func(request, _args, *_kwargs) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/utils/decorators.py", line 21, in bound_func return func(self, _args2, *_kwargs2) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1153, in changelist_view response = self.response_action(request, queryset=cl.get_query_set(request)) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/django/contrib/admin/options.py", line 908, in response_action response = func(self, request, queryset) File "/Users/sto/Sites/python.dev/python/stw_project/project/apps/contact/admin.py", line 63, in export_as_xls data = xlsDoc.writestr() File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/document_writers.py", line 21, in writestr return self.composer.run(child=child) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/composer_xls.py", line 220, in run self.iterate_grid() File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/composer_base.py", line 26, in iterate_grid self.write_cell(self.row_id, self.col_id, cell) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/composer_xls.py", line 165, in write_cell self.done_write_cell(row_id, col_id, cell, value, style) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/composer_xls.py", line 176, in done_write_cell new_width = get_string_width_from_style(value, style) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/composer_xls.py", line 19, in get_string_width_from_style return int(get_string_width(font_name, point_size, char_string) * 50) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/lib/font_data/decorators.py", line 21, in call value = self.func(*args) File "/Users/sto/Sites/python.dev/python/stw_project/env/lib/python2.7/site-packages/mm/lib/font_data/core.py", line 49, in get_string_width str_length = len(char_string) TypeError: object of type 'int' has no len()

    opened by SachaMPS 1
  • Extra Columns test fail

    Extra Columns test fail

    @hodgesds

    ERROR: test_extra_columns (test_extra_columns.TestColumns)

    Traceback (most recent call last): File "/Users/bray/mm/tests/test_extra_columns.py", line 36, in test_extra_columns self.check("test_extra_columns.xls", my_data) File "/Users/bray/mm/tests/test_extra_columns.py", line 45, in check cell_type = xls.get_type(row, col) return self.cell_types[str(worksheet.cell_type(row, colm))] File "/Users/bray/mm/mm_env/lib/python2.7/site-packages/xlrd/sheet.py", line 418, in cell_type return self._cell_types[rowx][colx] IndexError: array index out of range -------------------- >> begin captured logging << --------------------

    opened by brianray 1
  • Rename xlrd.py in test directory to xlrd_helper.py

    Rename xlrd.py in test directory to xlrd_helper.py

    Rename the xlrd.py to avoid the failing of tests

    C:\Users\Tathagata\Documents\GitHub\mm\tests>python gdata_tests.py
    Traceback (most recent call last):
     File "gdata_tests.py", line 6, in <module>
        from xlrd_helper import XLSReader
       ImportError: No module named xlrd_helper
    
    opened by tathagata 1
  • gdata_tests.py fails on Win 7, Python 2.7.2

    gdata_tests.py fails on Win 7, Python 2.7.2

    C:\Users\Tathagata\Documents\GitHub\mm\tests>python gdata_tests.py
    Google Username: C-3PO
    Google password:
    No handlers could be found for logger "mm.composer_xls"
    E
    ======================================================================
    ERROR: test_minimal (__main__.TestGdataSuite)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
     File "gdata_tests.py", line 45, in test_minimal
    password)
     File "C:\Python27\lib\site-packages\marmir-0.1.1-py2.7.egg\mm\document_writers.py", line 60, in write_gdata
    entry = gd_client.Upload(ms, name)
     File "C:\Python27\lib\site-packages\gdata\docs\service.py", line 306, in Upload
    folder_or_uri)
     File "C:\Python27\lib\site-packages\gdata\docs\service.py", line 161, in _UploadFile
    converter=gdata.docs.DocumentListEntryFromString)
     File "C:\Python27\lib\site-packages\gdata\service.py", line 1236, in Post
    media_source=media_source, converter=converter)
     File "C:\Python27\lib\site-packages\gdata\service.py", line 1358, in PostOrPut
    'reason': server_response.reason, 'body': result_body}
    RequestError: {'status': 400, 'body': 'The spreadsheet you are trying to upload is invalid.', 'reason': 'Bad Request'}
    
    ----------------------------------------------------------------------
    Ran 1 test in 11.664s
    
    FAILED (errors=1)
    

    Printing the values of the dict in the doc in line 36

        mm_doc = mm.Document(my_data)
        print mm_doc.__dict__.values()
    
     [<mm.grid_base.GridBase object at 0x0236BB50>, <mm.config_base.ConfigBase object at 0x0236BAD0>, <class 'mm.model_base.DataModel'>, [{'msg': 'My first Cell', 'when': datetime.datetime(2013, 2, 18, 19, 35, 11), 'id': 1}, {'msg': 'My second Cell', 'when': datetime.datetime(2013, 2, 18, 19, 35, 11), 'id': 2}], <mm.model_base.DataModel object at 0x0236BB10>]
    
    opened by tathagata 1
  • Fix Issues #24 and #25

    Fix Issues #24 and #25

    This is my pull request to fix issue #24 and #25.

    Issue #24 was about changing the syntax f = open(..) to with open(..) as f

    Issue #25 was about generating the *.xls files into their own subdirectory tests/generated_files

    A bunch of *_tests.py have been affected but the non-test files that have been affected are:

    document_writers.py
    textwidths.py
    

    All tests have passed as a result of these changes.

    opened by pebreo 0
  • Can it support python 3?

    Can it support python 3?

    It looks not support python3.

    $ pip install marmir
    Collecting marmir Using cached https://files.pythonhosted.org/packages/e4/7d/75d7c821c2a9439ccaad6f70d5cfb8ba3a7d5795b9632f3a9b89a3a40e2e/Marmir-0.1.3.tar.gz Complete output from command python setup.py egg_info: Traceback (most recent call last): File "", line 1, in File "/private/var/folders/n3/hnw9vdn51qzf29grjgmcyr800000gn/T/pip-install-8fun0y3l/marmir/setup.py", line 3, in import mm File "/private/var/folders/n3/hnw9vdn51qzf29grjgmcyr800000gn/T/pip-install-8fun0y3l/marmir/mm/init.py", line 10, in from document_base import * ModuleNotFoundError: No module named 'document_base'

    opened by hh-in-zhuzhou 10
  • catche and warn about more data than headers

    catche and warn about more data than headers

    https://github.com/brianray/mm/blob/master/mm/model_base.py#L125

    Catch the index error and raise a more specific exception. Add a setting to the config called 'INGORE_DATA_MISMATCH'--when True, log a warning instead of failing at this point.

    opened by brianray 0
Releases(mm3)
Owner
Brian Ray
Global Data Science Lead @brianray
Brian Ray
Solcast rooftop api for HA

Solcast Solar Home Assistant(https://www.home-assistant.io/) Component This custom component integrates the Solcast API into Home Assistant. Modified

Greg 1 Oct 11, 2021
A bot framework for Reddit to manage threads, wiki pages, widgets, menus and more.

Sub Manager Sub Manager is a bot framework for Reddit to automate a variety of tasks on one or more subreddits, and can be configured and run without

r/SpaceX 3 Aug 26, 2022
inventory replenishment for a hospital.

Inventory-Replenishment Inventory-Replenishment for a hospital that would like to explore how advanced anlytics may help automate their decision proce

1 Jan 09, 2022
You can connect with Sanila Ranatunga using this bot😉😉

Sanila-Ranatunga-s-Assistant-Bot You can connect with Sanila Ranatunga using this bot 😉 😉 Reach me on Telegram Sanila's Assistant Bot What is Telegr

Sanila Ranatunga 5 Feb 01, 2022
Client library for accessing IQM quantum computers

IQM Client Client-side library for connecting to an IQM quantum computer. Installation IQM client is not intended to be used directly by human users.

IQM 10 Dec 21, 2022
The official Discord Python framework for thenewboston blockchain.

Project Setup Follow the steps below to set up the project on your environment. Mac Setup Homebrew requires the Xcode command-line tools from Apple's

Bucky Roberts 18 Jul 15, 2022
A Python AWS Lambda Webhook listener that generates a permanent URL when an asset is created in Contentstack.

Webhook Listener A Python Lambda Webhook Listener - Generates a permanent URL on created assets. See doc on Generating a Permanent URL: https://www.co

Contentstack Solutions 1 Nov 04, 2021
Python library to download market data via Bloomberg, Eikon, Quandl, Yahoo etc.

findatapy findatapy creates an easy to use Python API to download market data from many sources including Quandl, Bloomberg, Yahoo, Google etc. using

Cuemacro 1.3k Jan 04, 2023
A python client for the Software-Challenge Germany.

sc-client-python A python client for the Software-Challenge Germany. Creating a new project (Optional) Install virtualenv virtualenv is a tool that cr

rpkak 3 Jan 22, 2022
🔮 Uncover some followers of a private instagram account

Private Instagram Chaining 🔮 Uncover part of followers of an instagram private account I have this private instagram account julianakhao. I need to g

аэт 69 Dec 17, 2022
Fetch torrent links from nyaa, according to releases by smoke index.

Nyaa - Smoke's index torrent fetcher Description This script parses the local (or online) anime release index (csv format) made by Big Smoke. And uses

Dinank 21 Jun 08, 2022
A Slack bot for playing Texas Hold 'Em where the currency is various workout tasks e.g. pushups

A Slack app/bot for playing Texas Hold 'Em where the currency is various workout tasks e.g. pushups. The intent is to make the workday more fun & active for remote teams.

Kyle McIntyre 3 Sep 19, 2022
Discord bot script for sending multiple media files to a discord channel according to discord limitations.

Discord Bulk Image Sending Bot Send bulk images to Discord channel. This is a bot script that will allow you to send multiple images to Discord channe

Nikola Arbov 1 Jan 13, 2022
Oussama has taken his first dose of vaccine D days ago

Oussama has taken his first dose of vaccine D days ago. He may take the second dose no less than L days and no more than R days since his first dose. Determine if Oussama is too early, too late, or i

INDIA - ENSAM Rabat 2 Feb 01, 2022
A Simple Google Translate Bot By VndGroup ❤️ Made With Python

VndGroup Google Translator Heroku Deploy ❤️ Functions This Bot Can Translate 95 Languages We Can Set Custom Language Group Support Mandatory Vars [+]

Venuja Sadew 1 Oct 09, 2022
Telegram client written in GTK & Python

Meowgram GTK Telegram Client 🐱 Why Meogram? Meowgram = Meow + Gram :D Meow - Talking cats sound. It's a symbol of unique and user friendly UI of clie

Artem Prokop 71 May 04, 2022
A file-based quote bot written in Python

Let's Write a Python Quote Bot! This repository will get you started with building a quote bot in Python. It's meant to be used along with the Learnin

1 Jan 15, 2022
Twitter bot to know the number of dislikes of a YouTube video

YT_dislikes is a twitter bot that allows you to know the number of dislikes (and likes) of a YouTube video. Now it is not possible to see the number o

1 Jan 08, 2022
Grade Notifyer Bot

A bot that automatically crawl the submission platform of montefiore to notify the student when a project has been graded.

Julien Gustin 2 Jun 02, 2022
Another Autoscaler is a Kubernetes controller that automatically starts, stops, or restarts pods from a deployment at a specified time using a cron annotation.

Another Autoscaler Another Autoscaler is a Kubernetes controller that automatically starts, stops, or restarts pods from a deployment at a specified t

Diego Najar 66 Nov 19, 2022